Merge branch 'antobinary-merge-webrtc-ds'
This commit is contained in:
commit
7125f4534d
@ -24,6 +24,8 @@ resolvers ++= Seq(
|
||||
"blindside-repos" at "http://blindside.googlecode.com/svn/repository/"
|
||||
)
|
||||
|
||||
resolvers += Resolver.sonatypeRepo("releases")
|
||||
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/dev/repo/maven-repo/releases" )) )
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
@ -38,20 +40,20 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/sc
|
||||
libraryDependencies ++= {
|
||||
val akkaVersion = "2.3.11"
|
||||
Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.7.2",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17-SNAPSHOT"
|
||||
)}
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.13" % "runtime",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.7.2",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17"
|
||||
)}
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
|
||||
|
@ -110,5 +110,12 @@ public interface IBigBlueButtonInGW {
|
||||
void undoWhiteboard(String meetingID, String requesterID, String whiteboardId);
|
||||
void enableWhiteboard(String meetingID, String requesterID, Boolean enable);
|
||||
void isWhiteboardEnabled(String meetingID, String requesterID, String replyTo);
|
||||
|
||||
|
||||
// DeskShare
|
||||
void deskShareStarted(String conferenceName, String callerId, String callerIdName);
|
||||
void deskShareStopped(String conferenceName, String callerId, String callerIdName);
|
||||
void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
void deskShareGetInfoRequest(String meetingId, String requesterId, String replyTo);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package org.bigbluebutton.core.pubsub.receivers;
|
||||
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonObject;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.bigbluebutton.common.messages.DeskShareStartedEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareStoppedEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStartedEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStoppedEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareGetInfoRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MessagingConstants;
|
||||
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
|
||||
|
||||
public class DeskShareMessageReceiver implements MessageHandler {
|
||||
|
||||
private IBigBlueButtonInGW bbbGW;
|
||||
private static final Logger log = LoggerFactory.getLogger(DeskShareMessageReceiver.class);
|
||||
public DeskShareMessageReceiver(IBigBlueButtonInGW bbbGW) {
|
||||
this.bbbGW = bbbGW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
if (channel.equalsIgnoreCase(MessagingConstants.FROM_VOICE_CONF_SYSTEM_CHAN)) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
|
||||
if (DeskShareStartedEventMessage.DESKSHARE_STARTED_MESSAGE.equals(messageName)) {
|
||||
DeskShareStartedEventMessage msg = DeskShareStartedEventMessage.fromJson(message);
|
||||
log.info("^^^^^^^DESKSHARE STARTED^^^^^^");
|
||||
bbbGW.deskShareStarted(msg.conferenceName, msg.callerId, msg.callerIdName);
|
||||
} else if (DeskShareStoppedEventMessage.DESK_SHARE_STOPPED_MESSAGE.equals(messageName)) {
|
||||
DeskShareStoppedEventMessage msg = DeskShareStoppedEventMessage.fromJson(message);
|
||||
log.info("^^^^^^^DESKSHARE STOPPED^^^^^^");
|
||||
bbbGW.deskShareStopped(msg.conferenceName, msg.callerId, msg.callerIdName);
|
||||
} else if (DeskShareRTMPBroadcastStartedEventMessage.DESKSHARE_RTMP_BROADCAST_STARTED_MESSAGE.equals(messageName)) {
|
||||
log.info("^^^^^^^DESKSHARE_RTMP_BROADCAST_STARTED_MESSAGE^^^^^^");
|
||||
DeskShareRTMPBroadcastStartedEventMessage msg = DeskShareRTMPBroadcastStartedEventMessage.fromJson(message);
|
||||
bbbGW.deskShareRTMPBroadcastStarted(msg.conferenceName, msg.streamname, msg.vw, msg.vh, msg.timestamp);
|
||||
} else if (DeskShareRTMPBroadcastStoppedEventMessage.DESKSHARE_RTMP_BROADCAST_STOPPED_MESSAGE.equals(messageName)) {
|
||||
log.info("^^^^^^^DESKSHARE_RTMP_BROADCAST_STOPPED_MESSAGE^^^^^^");
|
||||
DeskShareRTMPBroadcastStoppedEventMessage msg = DeskShareRTMPBroadcastStoppedEventMessage.fromJson(message);
|
||||
bbbGW.deskShareRTMPBroadcastStopped(msg.conferenceName, msg.streamname, msg.vw, msg.vh, msg.timestamp);
|
||||
} else if (DeskShareGetInfoRequestMessage.GET_DESKTOP_SHARE_GET_INFO_REQUEST.equals(messageName)) {
|
||||
log.info("^^^^^^^GET_DESKTOP_SHARE_INFO_REQUEST^^^^^^");
|
||||
DeskShareGetInfoRequestMessage msg = DeskShareGetInfoRequestMessage.fromJson(message);
|
||||
bbbGW.deskShareGetInfoRequest(msg.meetingId, msg.requesterId, msg.replyTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -45,10 +45,13 @@ public class RedisMessageReceiver {
|
||||
|
||||
WhiteboardMessageReceiver whiteboardRx = new WhiteboardMessageReceiver(bbbGW);
|
||||
receivers.add(whiteboardRx);
|
||||
|
||||
|
||||
DeskShareMessageReceiver deskShareRx = new DeskShareMessageReceiver(bbbGW);
|
||||
receivers.add(deskShareRx);
|
||||
|
||||
PollingMessageReceiver pollRx = new PollingMessageReceiver(bbbGW);
|
||||
receivers.add(pollRx);
|
||||
|
||||
|
||||
MeetingMessageReceiver meetingRx = new MeetingMessageReceiver(bbbGW);
|
||||
receivers.add(meetingRx);
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* 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.recorders.events;
|
||||
|
||||
import org.bigbluebutton.core.service.recorder.RecordEvent;
|
||||
|
||||
public abstract class AbstractDeskShareRecordEvent extends RecordEvent {
|
||||
|
||||
public AbstractDeskShareRecordEvent() {
|
||||
setModule("DESKSHARE");
|
||||
}
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
package org.bigbluebutton.core.recorders.events;
|
||||
|
||||
public class DeskShareNotifyViewersRTMPRecordEvent extends
|
||||
AbstractDeskShareRecordEvent {
|
||||
|
||||
public DeskShareNotifyViewersRTMPRecordEvent() {
|
||||
super();
|
||||
setEvent("DeskShareNotifyViewersRTMP");
|
||||
}
|
||||
|
||||
public void setStreamPath(String streamPath) {
|
||||
eventMap.put("streamPath", streamPath);
|
||||
}
|
||||
|
||||
public void setBroadcasting(Boolean broadcasting) {
|
||||
eventMap.put("broadcasting", broadcasting.toString());
|
||||
}
|
||||
|
||||
public void setVideoWidth(int videoWidth) {
|
||||
eventMap.put("videoWidth", Integer.toString(videoWidth));
|
||||
}
|
||||
|
||||
public void setVideoHeight(int videoHeight) {
|
||||
eventMap.put("videoHeight", Integer.toString(videoHeight));
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.core.recorders.events;
|
||||
|
||||
public class DeskShareStartRTMPRecordEvent extends
|
||||
AbstractDeskShareRecordEvent {
|
||||
|
||||
public DeskShareStartRTMPRecordEvent() {
|
||||
super();
|
||||
setEvent("DeskShareStartRTMP");
|
||||
}
|
||||
|
||||
public void setStreamPath(String streamPath) {
|
||||
eventMap.put("streamPath", streamPath);
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.core.recorders.events;
|
||||
|
||||
public class DeskShareStopRTMPRecordEvent extends
|
||||
AbstractDeskShareRecordEvent {
|
||||
|
||||
public DeskShareStopRTMPRecordEvent() {
|
||||
super();
|
||||
setEvent("DeskShareStopRTMP");
|
||||
}
|
||||
|
||||
public void setStreamPath(String streamPath) {
|
||||
eventMap.put("streamPath", streamPath);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package org.bigbluebutton.core.service.chat;
|
||||
|
||||
public class ChatKeyUtil {
|
||||
public static final String CHAT_TYPE = "chatType";
|
||||
public static final String FROM_USERID = "fromUserID";
|
||||
public static final String FROM_USERNAME = "fromUsername";
|
||||
public static final String FROM_COLOR = "fromColor";
|
||||
public static final String FROM_TIME = "fromTime";
|
||||
public static final String FROM_TZ_OFFSET = "fromTimezoneOffset";
|
||||
public static final String TO_USERID = "toUserID";
|
||||
public static final String TO_USERNAME = "toUsername";
|
||||
public static final String MESSAGE = "message";
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
package org.bigbluebutton.core.service.voice;
|
||||
|
||||
public class VoiceKeyUtil {
|
||||
public static final String MUTE = "mute";
|
||||
public static final String USERID = "userId";
|
||||
}
|
@ -33,4 +33,9 @@ redis {
|
||||
password=""
|
||||
# recording keys should expire in 14 days
|
||||
keyExpiry=1209600
|
||||
}
|
||||
}
|
||||
|
||||
red5 {
|
||||
deskshareip="192.168.0.109"
|
||||
deskshareapp="video-broadcast"
|
||||
}
|
||||
|
@ -29,11 +29,10 @@ object Boot extends App with SystemConfiguration {
|
||||
val recorderApp = new RecorderApplication(redisDispatcher)
|
||||
recorderApp.start()
|
||||
|
||||
val bbbInGW = new BigBlueButtonInGW(system, recorderApp, msgSender)
|
||||
val bbbInGW = new BigBlueButtonInGW(system, recorderApp, msgSender, red5DeskShareIP, red5DeskShareApp)
|
||||
val redisMsgReceiver = new RedisMessageReceiver(bbbInGW)
|
||||
|
||||
val redisSubscriberActor = system.actorOf(AppsRedisSubscriberActor.props(redisMsgReceiver), "redis-subscriber")
|
||||
|
||||
val keepAliveRedisPublisher = new KeepAliveRedisPublisher(system, redisPublisher)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -11,4 +11,6 @@ trait SystemConfiguration {
|
||||
lazy val redisPort = Try(config.getInt("redis.port")).getOrElse(6379)
|
||||
lazy val redisPassword = Try(config.getString("redis.password")).getOrElse("")
|
||||
lazy val keysExpiresInSec = Try(config.getInt("redis.keyExpiry")).getOrElse(14 * 86400) // 14 days
|
||||
lazy val red5DeskShareIP = Try(config.getString("red5.deskshareip")).getOrElse("127.0.0.1")
|
||||
lazy val red5DeskShareApp = Try(config.getString("red5.deskshareapp")).getOrElse("")
|
||||
}
|
@ -47,12 +47,20 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
|
||||
case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg)
|
||||
case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(msg)
|
||||
case msg: DeskShareStartedRequest => handleDeskShareStartedRequest(msg)
|
||||
case msg: DeskShareStoppedRequest => handleDeskShareStoppedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest => handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest => handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case msg: InMessage => handleMeetingMessage(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
private def findMeetingWithVoiceConfId(voiceConfId: String): Option[RunningMeeting] = {
|
||||
meetings.values.find(m => m.mProps.voiceBridge == voiceConfId)
|
||||
meetings.values.find(m => {
|
||||
println("+++ compare " + m.mProps.voiceBridge + " with our " + voiceConfId)
|
||||
m.mProps.voiceBridge == voiceConfId
|
||||
})
|
||||
}
|
||||
|
||||
private def handleUserJoinedVoiceConfMessage(msg: UserJoinedVoiceConfMessage) {
|
||||
@ -230,9 +238,61 @@ class BigBlueButtonActor(val system: ActorSystem, recorderApp: RecorderApplicati
|
||||
|
||||
//send lock settings
|
||||
self ! (new GetLockSettings(id, "nodeJSapp"))
|
||||
|
||||
//send desktop sharing info
|
||||
self ! (new DeskShareGetDeskShareInfoRequest(id, "nodeJSapp", "nodeJSapp"))
|
||||
|
||||
}
|
||||
|
||||
outGW.send(new GetAllMeetingsReply(resultArray))
|
||||
}
|
||||
|
||||
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
|
||||
log.info("handleDeskShareStartedRequest: msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareStartedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
|
||||
log.info("handleDeskShareStoppedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareStoppedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStartedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStoppedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
val m = meetings.values.find(m => {
|
||||
m.mProps.meetingID == msg.conferenceName
|
||||
})
|
||||
m foreach { mActor => mActor.actorRef ! msg }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,9 @@ import org.bigbluebutton.common.messages.IBigBlueButtonMessage
|
||||
import org.bigbluebutton.common.messages.StartCustomPollRequestMessage
|
||||
import org.bigbluebutton.common.messages.PubSubPingMessage
|
||||
|
||||
class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender) extends IBigBlueButtonInGW {
|
||||
class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplication, messageSender: MessageSender,
|
||||
val red5DeskShareIP: String, val red5DeskShareApp: String) extends IBigBlueButtonInGW {
|
||||
|
||||
val log = system.log
|
||||
val bbbActor = system.actorOf(BigBlueButtonActor.props(system, recorderApp, messageSender), "bigbluebutton-actor")
|
||||
|
||||
@ -41,7 +43,7 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
|
||||
val mProps = new MeetingProperties(meetingID, externalMeetingID, meetingName, record,
|
||||
voiceBridge, duration, autoStartRecording, allowStartStopRecording,
|
||||
moderatorPass, viewerPass, createTime, createDate)
|
||||
moderatorPass, viewerPass, createTime, createDate, red5DeskShareIP, red5DeskShareApp)
|
||||
bbbActor ! new CreateMeeting(meetingID, mProps)
|
||||
}
|
||||
|
||||
@ -443,6 +445,31 @@ class BigBlueButtonInGW(val system: ActorSystem, recorderApp: RecorderApplicatio
|
||||
bbbActor ! new VoiceConfRecordingStartedMessage(voiceConfId, recordingFile, recording, timestamp)
|
||||
}
|
||||
|
||||
/**
|
||||
* *******************************************************************
|
||||
* Message Interface for DeskShare
|
||||
* *****************************************************************
|
||||
*/
|
||||
def deskShareStarted(conferenceName: String, callerId: String, callerIdName: String) {
|
||||
bbbActor ! new DeskShareStartedRequest(conferenceName, callerId, callerIdName)
|
||||
}
|
||||
|
||||
def deskShareStopped(conferenceName: String, callerId: String, callerIdName: String) {
|
||||
bbbActor ! new DeskShareStoppedRequest(conferenceName, callerId, callerIdName)
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStarted(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
bbbActor ! new DeskShareRTMPBroadcastStartedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStopped(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String) {
|
||||
bbbActor ! new DeskShareRTMPBroadcastStoppedRequest(conferenceName, streamname, videoWidth, videoHeight, timestamp)
|
||||
}
|
||||
|
||||
def deskShareGetInfoRequest(meetingId: String, requesterId: String, replyTo: String): Unit = {
|
||||
bbbActor ! new DeskShareGetDeskShareInfoRequest(meetingId, requesterId, replyTo)
|
||||
}
|
||||
|
||||
// Polling
|
||||
def votePoll(meetingId: String, userId: String, pollId: String, questionId: Integer, answerId: Integer) {
|
||||
bbbActor ! new RespondToPollRequest(meetingId, userId, pollId, questionId, answerId)
|
||||
|
@ -56,6 +56,16 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
|
||||
handleUserTalkingInVoiceConfMessage(msg)
|
||||
case msg: VoiceConfRecordingStartedMessage =>
|
||||
handleVoiceConfRecordingStartedMessage(msg)
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest =>
|
||||
handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest =>
|
||||
handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareStartedRequest =>
|
||||
handleDeskShareStartedRequest(msg)
|
||||
case msg: DeskShareStoppedRequest =>
|
||||
handleDeskShareStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest =>
|
||||
handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case msg: UserJoining =>
|
||||
handleUserJoin(msg)
|
||||
case msg: UserLeaving =>
|
||||
@ -221,11 +231,12 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
|
||||
}
|
||||
|
||||
def handleMonitorNumberOfWebUsers() {
|
||||
// println("BACK TIMER")
|
||||
// println("BACK TIMER")
|
||||
if (usersModel.numWebUsers == 0 && meetingModel.lastWebUserLeftOn > 0) {
|
||||
if (timeNowInMinutes - meetingModel.lastWebUserLeftOn > 2) {
|
||||
log.info("Empty meeting. Ejecting all users from voice. meetingId={}", mProps.meetingID)
|
||||
outGW.send(new EjectAllVoiceUsers(mProps.meetingID, mProps.recorded, mProps.voiceBridge))
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,4 +303,74 @@ class MeetingActor(val mProps: MeetingProperties, val outGW: OutMessageGateway)
|
||||
meetingModel.permissionsEqual(other)
|
||||
}
|
||||
|
||||
}
|
||||
// Broadcast video stream,
|
||||
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
||||
if (!meetingModel.getDeskShareStarted()) {
|
||||
val timestamp = System.currentTimeMillis().toString()
|
||||
val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
|
||||
"/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
|
||||
log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
|
||||
|
||||
// Tell FreeSwitch to broadcast to RTMP
|
||||
outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
|
||||
|
||||
meetingModel.setDeskShareStarted(true)
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
|
||||
log.info("handleDeskShareStoppedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
||||
// Tell FreeSwitch to stop broadcasting to RTMP
|
||||
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, meetingModel.getRTMPBroadcastingUrl()))
|
||||
|
||||
meetingModel.setDeskShareStarted(false)
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
|
||||
|
||||
// only valid if not broadcasting yet
|
||||
if (!meetingModel.isBroadcastingRTMP()) {
|
||||
meetingModel.setRTMPBroadcastingUrl(msg.streamname)
|
||||
meetingModel.broadcastingRTMPStarted()
|
||||
meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
|
||||
meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
|
||||
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
|
||||
|
||||
// Notify viewers in the meeting that there's an rtmp stream to view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
|
||||
} else {
|
||||
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
|
||||
|
||||
// only valid if currently broadcasting
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
|
||||
meetingModel.broadcastingRTMPStopped()
|
||||
|
||||
// notify viewers that RTMP broadcast stopped
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
msg.videoWidth, msg.videoHeight, false))
|
||||
} else {
|
||||
log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" + meetingModel.isBroadcastingRTMP())
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
// if the meeting has an ongoing WebRTC Deskshare session, send a notification
|
||||
outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), true))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -6,13 +6,14 @@ import java.util.concurrent.TimeUnit
|
||||
case object StopMeetingActor
|
||||
case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
|
||||
voiceBridge: String, duration: Long, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String)
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String, red5DeskShareIP: String, red5DeskShareApp: String)
|
||||
|
||||
class MeetingModel {
|
||||
private var audioSettingsInited = false
|
||||
private var permissionsInited = false
|
||||
private var permissions = new Permissions()
|
||||
private var recording = false;
|
||||
private var broadcastingRTMP = false
|
||||
private var muted = false;
|
||||
private var meetingEnded = false
|
||||
private var meetingMuted = false
|
||||
@ -22,9 +23,46 @@ class MeetingModel {
|
||||
private var lastWebUserLeftOnTimestamp: Long = 0
|
||||
|
||||
private var voiceRecordingFilename: String = ""
|
||||
private var rtmpBroadcastingUrl: String = ""
|
||||
private var deskShareStarted = false
|
||||
private var desktopShareVideoWidth = 0
|
||||
private var desktopShareVideoHeight = 0
|
||||
|
||||
val startedOn = timeNowInMinutes;
|
||||
|
||||
def resetDesktopSharingParams() = {
|
||||
broadcastingRTMP = false
|
||||
deskShareStarted = false
|
||||
rtmpBroadcastingUrl = ""
|
||||
desktopShareVideoWidth = 0
|
||||
desktopShareVideoHeight = 0
|
||||
}
|
||||
|
||||
def getDeskShareStarted(): Boolean = {
|
||||
return deskShareStarted
|
||||
}
|
||||
|
||||
def setDeskShareStarted(b: Boolean) {
|
||||
deskShareStarted = b
|
||||
println("---deskshare status changed to:" + b)
|
||||
}
|
||||
|
||||
def setDesktopShareVideoWidth(videoWidth: Int) {
|
||||
desktopShareVideoWidth = videoWidth
|
||||
}
|
||||
|
||||
def setDesktopShareVideoHeight(videoHeight: Int) {
|
||||
desktopShareVideoHeight = videoHeight
|
||||
}
|
||||
|
||||
def getDesktopShareVideoWidth(): Int = {
|
||||
desktopShareVideoWidth
|
||||
}
|
||||
|
||||
def getDesktopShareVideoHeight(): Int = {
|
||||
desktopShareVideoHeight
|
||||
}
|
||||
|
||||
def muteMeeting() {
|
||||
meetingMuted = true
|
||||
}
|
||||
@ -49,6 +87,18 @@ class MeetingModel {
|
||||
recording
|
||||
}
|
||||
|
||||
def broadcastingRTMPStarted() {
|
||||
broadcastingRTMP = true
|
||||
}
|
||||
|
||||
def isBroadcastingRTMP(): Boolean = {
|
||||
broadcastingRTMP
|
||||
}
|
||||
|
||||
def broadcastingRTMPStopped() {
|
||||
broadcastingRTMP = false
|
||||
}
|
||||
|
||||
def lastWebUserLeft() {
|
||||
lastWebUserLeftOnTimestamp = timeNowInMinutes
|
||||
}
|
||||
@ -69,6 +119,15 @@ class MeetingModel {
|
||||
voiceRecordingFilename
|
||||
}
|
||||
|
||||
def setRTMPBroadcastingUrl(path: String) {
|
||||
println("---RTMP broadcastUrl changed to:" + path)
|
||||
rtmpBroadcastingUrl = path
|
||||
}
|
||||
|
||||
def getRTMPBroadcastingUrl(): String = {
|
||||
rtmpBroadcastingUrl
|
||||
}
|
||||
|
||||
def permisionsInitialized(): Boolean = {
|
||||
permissionsInited
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.MeetingMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.PesentationMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.DeskShareMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
@ -114,6 +115,11 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
|
||||
case msg: WhiteboardEnabledEvent => handleWhiteboardEnabledEvent(msg)
|
||||
case msg: IsWhiteboardEnabledReply => handleIsWhiteboardEnabledReply(msg)
|
||||
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
|
||||
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
|
||||
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
|
||||
case msg: DeskShareNotifyASingleViewer => handleDeskShareNotifyASingleViewer(msg)
|
||||
case msg: DeskShareHangUp => handleDeskShareHangUp(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
@ -122,6 +128,31 @@ class MessageSenderActor(val meetingId: String, val service: MessageSender)
|
||||
service.send(MessagingConstants.FROM_USERS_CHANNEL, m.toJson)
|
||||
}
|
||||
|
||||
private def handleDeskShareHangUp(msg: DeskShareHangUp) {
|
||||
val json = DeskShareMessageToJsonConverter.getDeskShareHangUpToJson(msg)
|
||||
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, json)
|
||||
}
|
||||
|
||||
private def handleDeskShareStopRTMPBroadcast(msg: DeskShareStopRTMPBroadcast) {
|
||||
val json = DeskShareMessageToJsonConverter.getDeskShareStopRTMPBroadcastToJson(msg)
|
||||
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, json)
|
||||
}
|
||||
|
||||
private def handleDeskShareNotifyViewersRTMP(msg: DeskShareNotifyViewersRTMP) {
|
||||
val json = DeskShareMessageToJsonConverter.getDeskShareNotifyViewersRTMPToJson(msg)
|
||||
service.send(MessagingConstants.FROM_DESK_SHARE_CHANNEL, json)
|
||||
}
|
||||
|
||||
def handleDeskShareNotifyASingleViewer(msg: DeskShareNotifyASingleViewer) {
|
||||
val json = DeskShareMessageToJsonConverter.getDeskShareNotifyASingleViewerToJson(msg)
|
||||
service.send(MessagingConstants.FROM_DESK_SHARE_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleDeskShareStartRTMPBroadcast(msg: DeskShareStartRTMPBroadcast) {
|
||||
val json = DeskShareMessageToJsonConverter.getDeskShareStartRTMPBroadcastToJson(msg)
|
||||
service.send(MessagingConstants.TO_VOICE_CONF_SYSTEM_CHAN, json)
|
||||
}
|
||||
|
||||
private def handleGetChatHistoryReply(msg: GetChatHistoryReply) {
|
||||
val json = ChatMessageToJsonConverter.getChatHistoryReplyToJson(msg)
|
||||
service.send(MessagingConstants.FROM_CHAT_CHANNEL, json)
|
||||
|
@ -29,7 +29,11 @@ import org.bigbluebutton.core.recorders.events.ParticipantEndAndKickAllRecordEve
|
||||
import org.bigbluebutton.core.recorders.events.UndoShapeWhiteboardRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.ClearPageWhiteboardRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.AddShapeWhiteboardRecordEvent
|
||||
import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.core.recorders.events.DeskShareStartRTMPRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.DeskShareStopRTMPRecordEvent
|
||||
import org.bigbluebutton.core.recorders.events.DeskShareNotifyViewersRTMPRecordEvent
|
||||
// import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.core.recorders.events.ModifyTextWhiteboardRecordEvent
|
||||
import scala.collection.immutable.StringOps
|
||||
|
||||
@ -67,6 +71,9 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
case msg: SendWhiteboardAnnotationEvent => handleSendWhiteboardAnnotationEvent(msg)
|
||||
case msg: ClearWhiteboardEvent => handleClearWhiteboardEvent(msg)
|
||||
case msg: UndoWhiteboardEvent => handleUndoWhiteboardEvent(msg)
|
||||
case msg: DeskShareStartRTMPBroadcast => handleDeskShareStartRTMPBroadcast(msg)
|
||||
case msg: DeskShareStopRTMPBroadcast => handleDeskShareStopRTMPBroadcast(msg)
|
||||
case msg: DeskShareNotifyViewersRTMP => handleDeskShareNotifyViewersRTMP(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
@ -434,4 +441,34 @@ class RecorderActor(val meetingId: String, val recorder: RecorderApplication)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private def handleDeskShareStartRTMPBroadcast(msg: DeskShareStartRTMPBroadcast) {
|
||||
val event = new DeskShareStartRTMPRecordEvent()
|
||||
event.setMeetingId(msg.conferenceName)
|
||||
event.setStreamPath(msg.streamPath)
|
||||
event.setTimestamp(TimestampGenerator.generateTimestamp)
|
||||
log.info("handleDeskShareStartRTMPBroadcast " + msg.conferenceName)
|
||||
recorder.record(msg.conferenceName, event)
|
||||
}
|
||||
|
||||
private def handleDeskShareStopRTMPBroadcast(msg: DeskShareStopRTMPBroadcast) {
|
||||
val event = new DeskShareStopRTMPRecordEvent()
|
||||
event.setMeetingId(msg.conferenceName)
|
||||
event.setStreamPath(msg.streamPath)
|
||||
event.setTimestamp(TimestampGenerator.generateTimestamp)
|
||||
log.info("handleDeskShareStopRTMPBroadcast " + msg.conferenceName)
|
||||
recorder.record(msg.conferenceName, event)
|
||||
}
|
||||
|
||||
private def handleDeskShareNotifyViewersRTMP(msg: DeskShareNotifyViewersRTMP) {
|
||||
val event = new DeskShareNotifyViewersRTMPRecordEvent()
|
||||
event.setMeetingId(msg.meetingID)
|
||||
event.setStreamPath(msg.streamPath)
|
||||
event.setBroadcasting(msg.broadcasting)
|
||||
event.setTimestamp(TimestampGenerator.generateTimestamp)
|
||||
|
||||
log.info("handleDeskShareNotifyViewersRTMP " + msg.meetingID)
|
||||
recorder.record(msg.meetingID, event)
|
||||
}
|
||||
|
||||
}
|
@ -115,3 +115,11 @@ case class UndoWhiteboardRequest(meetingID: String, requesterID: String, whitebo
|
||||
case class EnableWhiteboardRequest(meetingID: String, requesterID: String, enable: Boolean) extends InMessage
|
||||
case class IsWhiteboardEnabledRequest(meetingID: String, requesterID: String, replyTo: String) extends InMessage
|
||||
case class GetAllMeetingsRequest(meetingID: String /** Not used. Just to satisfy trait **/ ) extends InMessage
|
||||
|
||||
// DeskShare
|
||||
case class DeskShareStartedRequest(conferenceName: String, callerId: String, callerIdName: String)
|
||||
case class DeskShareStoppedRequest(conferenceName: String, callerId: String, callerIdName: String)
|
||||
case class DeskShareRTMPBroadcastStartedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
|
||||
case class DeskShareRTMPBroadcastStoppedRequest(conferenceName: String, streamname: String, videoWidth: Int, videoHeight: Int, timestamp: String)
|
||||
case class DeskShareGetDeskShareInfoRequest(conferenceName: String, requesterID: String, replyTo: String)
|
||||
|
||||
|
@ -130,6 +130,13 @@ case class WhiteboardEnabledEvent(meetingID: String, recorded: Boolean, requeste
|
||||
case class IsWhiteboardEnabledReply(meetingID: String, recorded: Boolean, requesterID: String, enabled: Boolean, replyTo: String) extends IOutMessage
|
||||
case class GetAllMeetingsReply(meetings: Array[MeetingInfo]) extends IOutMessage
|
||||
|
||||
// DeskShare
|
||||
case class DeskShareStartRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage
|
||||
case class DeskShareStopRTMPBroadcast(conferenceName: String, streamPath: String) extends IOutMessage
|
||||
case class DeskShareNotifyViewersRTMP(meetingID: String, streamPath: String, videoWidth: Int, videoHeight: Int, broadcasting: Boolean) extends IOutMessage
|
||||
case class DeskShareNotifyASingleViewer(meetingID: String, userID: String, streamPath: String, videoWidth: Int, videoHeight: Int, broadcasting: Boolean) extends IOutMessage
|
||||
case class DeskShareHangUp(meetingID: String, fsConferenceName: String) extends IOutMessage
|
||||
|
||||
// Value Objects
|
||||
case class MeetingVO(id: String, recorded: Boolean)
|
||||
|
||||
|
@ -132,4 +132,9 @@ case class MeetingPasswords(moderatorPass: String, viewerPass: String)
|
||||
case class MeetingDuration(duration: Int = 0, createdTime: Long = 0,
|
||||
startTime: Long = 0, endTime: Long = 0)
|
||||
|
||||
case class MeetingInfo(meetingID: String, meetingName: String, recorded: Boolean, voiceBridge: String, duration: Long)
|
||||
case class MeetingInfo(
|
||||
meetingID: String,
|
||||
meetingName: String,
|
||||
recorded: Boolean,
|
||||
voiceBridge: String,
|
||||
duration: Long)
|
||||
|
@ -4,7 +4,8 @@ import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import scala.collection.mutable.HashMap
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
// import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import com.google.gson.Gson
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
|
@ -246,6 +246,18 @@ trait UsersApp {
|
||||
+ ". Making user=[" + mod.userID + "] presenter.")
|
||||
assignNewPresenter(mod.userID, mod.name, mod.userID)
|
||||
}
|
||||
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
// The presenter left during desktop sharing. Stop desktop sharing on FreeSWITCH
|
||||
outGW.send(new DeskShareHangUp(mProps.meetingID, mProps.voiceBridge))
|
||||
|
||||
// notify other clients to close their deskshare view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), false))
|
||||
|
||||
// reset meeting info
|
||||
meetingModel.resetDesktopSharingParams()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -399,9 +411,9 @@ trait UsersApp {
|
||||
* and is reconnecting. Make the user as joined only in the voice conference. If we get a
|
||||
* user left voice conference message, then we will remove the user from the users list.
|
||||
*/
|
||||
switchUserToPhoneUser((new UserJoinedVoiceConfMessage(mProps.voiceBridge,
|
||||
switchUserToPhoneUser(new UserJoinedVoiceConfMessage(mProps.voiceBridge,
|
||||
vu.userId, u.userID, u.externUserID, vu.callerName,
|
||||
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly)));
|
||||
vu.callerNum, vu.muted, vu.talking, vu.avatarURL, u.listenOnly));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@ package org.bigbluebutton.core.apps
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.core.MeetingActor
|
||||
import org.bigbluebutton.core.service.whiteboard.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.common.messages.WhiteboardKeyUtil
|
||||
import org.bigbluebutton.core.OutMessageGateway
|
||||
|
||||
case class Whiteboard(id: String, shapes: Seq[AnnotationVO])
|
||||
@ -90,4 +90,4 @@ trait WhiteboardApp {
|
||||
val enabled = wbModel.isWhiteboardEnabled()
|
||||
outGW.send(new IsWhiteboardEnabledReply(mProps.meetingID, mProps.recorded, msg.requesterID, enabled, msg.replyTo))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import scala.collection.JavaConversions._
|
||||
import java.util.ArrayList
|
||||
import org.bigbluebutton.common.messages.MessagingConstants
|
||||
import org.bigbluebutton.core.messaging.Util
|
||||
import org.bigbluebutton.core.service.chat.ChatKeyUtil
|
||||
import org.bigbluebutton.common.messages.ChatKeyUtil
|
||||
|
||||
object ChatMessageToJsonConverter {
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.bigbluebutton.core.pubsub.senders
|
||||
|
||||
import org.bigbluebutton.core.api._
|
||||
import org.bigbluebutton.common.messages.DeskShareStartRTMPBroadcastEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareStopRTMPBroadcastEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareHangUpEventMessage
|
||||
|
||||
object DeskShareMessageToJsonConverter {
|
||||
def getDeskShareHangUpToJson(msg: DeskShareHangUp): String = {
|
||||
val newMsg = new DeskShareHangUpEventMessage(msg.meetingID, msg.fsConferenceName, TimestampGenerator.getCurrentTime.toString())
|
||||
newMsg.toJson()
|
||||
}
|
||||
|
||||
def getDeskShareNotifyASingleViewerToJson(msg: DeskShareNotifyASingleViewer): String = {
|
||||
val newMsg = new DeskShareNotifyASingleViewerEventMessage(msg.meetingID, msg.userID,
|
||||
msg.streamPath, msg.broadcasting, msg.videoWidth, msg.videoHeight, TimestampGenerator.getCurrentTime.toString())
|
||||
newMsg.toJson()
|
||||
}
|
||||
|
||||
def getDeskShareStartRTMPBroadcastToJson(msg: DeskShareStartRTMPBroadcast): String = {
|
||||
val newMsg = new DeskShareStartRTMPBroadcastEventMessage(msg.conferenceName, msg.streamPath,
|
||||
TimestampGenerator.getCurrentTime.toString())
|
||||
newMsg.toJson()
|
||||
}
|
||||
|
||||
def getDeskShareStopRTMPBroadcastToJson(msg: DeskShareStopRTMPBroadcast): String = {
|
||||
val newMsg = new DeskShareStopRTMPBroadcastEventMessage(msg.conferenceName, msg.streamPath,
|
||||
TimestampGenerator.getCurrentTime.toString())
|
||||
newMsg.toJson()
|
||||
}
|
||||
|
||||
def getDeskShareNotifyViewersRTMPToJson(msg: DeskShareNotifyViewersRTMP): String = {
|
||||
val newMsg = new DeskShareNotifyViewersRTMPEventMessage(msg.meetingID, msg.streamPath,
|
||||
msg.broadcasting, msg.videoWidth, msg.videoHeight, TimestampGenerator.getCurrentTime.toString())
|
||||
newMsg.toJson()
|
||||
}
|
||||
}
|
@ -24,6 +24,8 @@ resolvers ++= Seq(
|
||||
"blindside-repos" at "http://blindside.googlecode.com/svn/repository/"
|
||||
)
|
||||
|
||||
resolvers += Resolver.sonatypeRepo("releases")
|
||||
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/dev/repo/maven-repo/releases" )) )
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
@ -38,21 +40,21 @@ testOptions in Test += Tests.Argument(TestFrameworks.ScalaTest, "-h", "target/sc
|
||||
libraryDependencies ++= {
|
||||
val akkaVersion = "2.3.11"
|
||||
Seq(
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.3",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.1.0",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17-SNAPSHOT",
|
||||
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.3"
|
||||
)}
|
||||
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
|
||||
"com.typesafe.akka" %% "akka-testkit" % akkaVersion % "test",
|
||||
"com.typesafe.akka" %% "akka-slf4j" % akkaVersion,
|
||||
"ch.qos.logback" % "logback-classic" % "1.0.3",
|
||||
"org.pegdown" % "pegdown" % "1.4.0",
|
||||
"junit" % "junit" % "4.11",
|
||||
"com.etaty.rediscala" %% "rediscala" % "1.4.0",
|
||||
"commons-codec" % "commons-codec" % "1.8",
|
||||
"joda-time" % "joda-time" % "2.3",
|
||||
"com.google.code.gson" % "gson" % "1.7.1",
|
||||
"redis.clients" % "jedis" % "2.1.0",
|
||||
"org.apache.commons" % "commons-lang3" % "3.2",
|
||||
"org.bigbluebutton" % "bbb-common-message" % "0.0.17",
|
||||
"org.bigbluebutton" % "bbb-fsesl-client" % "0.0.4"
|
||||
)}
|
||||
|
||||
seq(Revolver.settings: _*)
|
||||
|
||||
|
@ -0,0 +1,73 @@
|
||||
<!-- http://wiki.freeswitch.org/wiki/Mod_conference -->
|
||||
<!-- None of these paths are real if you want any of these options you need to really set them up -->
|
||||
<configuration name="conference.conf" description="Audio Conference">
|
||||
<!-- Advertise certain presence on startup . -->
|
||||
<advertise>
|
||||
<room name="3001@$${domain}" status="FreeSWITCH"/>
|
||||
</advertise>
|
||||
|
||||
<!-- These are the default keys that map when you do not specify a caller control group -->
|
||||
<!-- Note: none and default are reserved names for group names. Disabled if dist-dtmf member flag is set. -->
|
||||
<caller-controls>
|
||||
<group name="default">
|
||||
<control action="mute" digits="0"/>
|
||||
<control action="deaf mute" digits="*"/>
|
||||
<control action="energy up" digits="9"/>
|
||||
<control action="energy equ" digits="8"/>
|
||||
<control action="energy dn" digits="7"/>
|
||||
<control action="vol talk up" digits="3"/>
|
||||
<control action="vol talk zero" digits="2"/>
|
||||
<control action="vol talk dn" digits="1"/>
|
||||
<control action="vol listen up" digits="6"/>
|
||||
<control action="vol listen zero" digits="5"/>
|
||||
<control action="vol listen dn" digits="4"/>
|
||||
<control action="hangup" digits="#"/>
|
||||
</group>
|
||||
</caller-controls>
|
||||
|
||||
<!-- Profiles are collections of settings you can reference by name. -->
|
||||
<profiles>
|
||||
|
||||
<!-- profile used for WebRTC Desktop Sharing -->
|
||||
<profile name="video-mcu-stereo">
|
||||
<param name="domain" value="$${domain}"/>
|
||||
<param name="rate" value="48000"/>
|
||||
<param name="channels" value="2"/>
|
||||
<param name="interval" value="20"/>
|
||||
<param name="energy-level" value="200"/>
|
||||
<!-- <param name="tts-engine" value="flite"/> -->
|
||||
<!-- <param name="tts-voice" value="kal16"/> -->
|
||||
|
||||
<!--remove audio for when user is alone since we hit this case every time-->
|
||||
<!--with -DESKSHARE conference. It has a single user only by default-->
|
||||
|
||||
<!--<param name="muted-sound" value="conference/conf-muted.wav"/>-->
|
||||
<!--<param name="unmuted-sound" value="conference/conf-unmuted.wav"/>-->
|
||||
<!-- <param name="alone-sound" value="conference/conf-alone.wav"/> -->
|
||||
<!-- <param name="moh-sound" value="local_stream://stereo"/> -->
|
||||
<!--<param name="enter-sound" value="tone_stream://%(200,0,500,600,700)"/>-->
|
||||
<!--<param name="exit-sound" value="tone_stream://%(500,0,300,200,100,50,25)"/>-->
|
||||
<!--<param name="kicked-sound" value="conference/conf-kicked.wav"/>-->
|
||||
<!--<param name="locked-sound" value="conference/conf-locked.wav"/>-->
|
||||
<!--<param name="is-locked-sound" value="conference/conf-is-locked.wav"/>-->
|
||||
<!--<param name="is-unlocked-sound" value="conference/conf-is-unlocked.wav"/>-->
|
||||
<!--<param name="pin-sound" value="conference/conf-pin.wav"/>-->
|
||||
<!--<param name="bad-pin-sound" value="conference/conf-bad-pin.wav"/>-->
|
||||
|
||||
<param name="caller-id-name" value="$${outbound_caller_name}"/>
|
||||
<param name="caller-id-number" value="$${outbound_caller_id}"/>
|
||||
<param name="comfort-noise" value="false"/>
|
||||
<param name="conference-flags" value="video-floor-only|video-required-for-canvas|rfc-4579|livearray-sync|minimize-video-encoding"/>
|
||||
|
||||
<param name="video-mode" value="mux"/> <!-- other values for video-mode are transcode or passthrough -->
|
||||
<param name="video-layout-name" value="1x1"/> <!-- 1x1 since we only have 1 video stream -->
|
||||
<param name="video-layout-name" value="group:grid"/>
|
||||
<param name="video-canvas-size" value="1920x1080"/>
|
||||
<param name="video-canvas-bgcolor" value="#333333"/>
|
||||
<param name="video-layout-bgcolor" value="#000000"/>
|
||||
<param name="video-codec-bandwidth" value="1mb"/>
|
||||
<param name="video-fps" value="15"/>
|
||||
</profile>
|
||||
|
||||
</profiles>
|
||||
</configuration>
|
@ -0,0 +1,30 @@
|
||||
<configuration name="verto.conf" description="HTML5 Verto Endpoint">
|
||||
|
||||
<settings>
|
||||
<param name="debug" value="10"/>
|
||||
</settings>
|
||||
|
||||
<profiles>
|
||||
<profile name="mine">
|
||||
<param name="bind-local" value="0.0.0.0:8081"/>
|
||||
<param name="bind-local" value="0.0.0.0:8082" secure="true"/>
|
||||
<param name="force-register-domain" value="$${domain}"/>
|
||||
<param name="secure-combined" value="$${certs_dir}/wss.pem"/>
|
||||
<param name="secure-chain" value="$${certs_dir}/wss.pem"/>
|
||||
<param name="userauth" value="true"/>
|
||||
<!-- setting this to true will allow anyone to register even with no account so use with care -->
|
||||
<param name="blind-reg" value="false"/>
|
||||
<param name="mcast-ip" value="224.1.1.1"/>
|
||||
<param name="mcast-port" value="1337"/>
|
||||
<param name="rtp-ip" value="$${local_ip_v4}"/>
|
||||
<!-- <param name="ext-rtp-ip" value=""/> -->
|
||||
<param name="local-network" value="localnet.auto"/>
|
||||
<param name="outbound-codec-string" value="opus,vp8"/>
|
||||
<param name="inbound-codec-string" value="opus,vp8"/>
|
||||
<param name="apply-candidate-acl" value="wan.auto"/>
|
||||
<param name="timer-name" value="soft"/>
|
||||
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</configuration>
|
34
akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml
Normal file
34
akka-bbb-fsesl/fs_conf_files/conf/dialplan/default.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
NOTICE:
|
||||
|
||||
This context is usually accessed via authenticated callers on the sip profile on port 5060
|
||||
or transfered callers from the public context which arrived via the sip profile on port 5080.
|
||||
|
||||
Authenticated users will use the user_context variable on the user to determine what context
|
||||
they can access. You can also add a user in the directory with the cidr= attribute acl.conf.xml
|
||||
will build the domains ACL using this value.
|
||||
-->
|
||||
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
|
||||
<include>
|
||||
<context name="default">
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}$">
|
||||
<action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/>
|
||||
<action application="transfer" data="${destination_number} XML public"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^(\d{5})(-screen)$">
|
||||
<action application="log" data="INFO BB $1 $2 BBBB transferring to $1 XML public!!"/>
|
||||
<action application="transfer" data="$1 XML public"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<!-- other extensions -->
|
||||
|
||||
</context>
|
||||
</include>
|
||||
|
32
akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml
Normal file
32
akka-bbb-fsesl/fs_conf_files/conf/dialplan/public.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<!--
|
||||
NOTICE:
|
||||
|
||||
This context is usually accessed via the external sip profile listening on port 5080.
|
||||
|
||||
It is recommended to have separate inbound and outbound contexts. Not only for security
|
||||
but clearing up why you would need to do such a thing. You don't want outside un-authenticated
|
||||
callers hitting your default context which allows dialing calls thru your providers and results
|
||||
in Toll Fraud.
|
||||
-->
|
||||
|
||||
<!-- http://wiki.freeswitch.org/wiki/Dialplan_XML -->
|
||||
<include>
|
||||
<context name="public">
|
||||
|
||||
<!-- other extensions -->
|
||||
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}$">
|
||||
<action application="log" data="INFO ************ redirecting ${destination_number} to ${destination_number}-DESKSHARE@video-mcu-stereo ***********" />
|
||||
<action application="answer"/>
|
||||
<action application="conference" data="${destination_number}-DESKSHARE@video-mcu-stereo"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<!--
|
||||
You can place files in the public directory to get included.
|
||||
-->
|
||||
<X-PRE-PROCESS cmd="include" data="public/*.xml"/>
|
||||
|
||||
</context>
|
||||
</include>
|
@ -7,6 +7,9 @@ import org.bigbluebutton.common.messages.GetUsersFromVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.MuteUserInVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.StartRecordingVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.StopRecordingVoiceConfRequestMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareStartRTMPBroadcastEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareStopRTMPBroadcastEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareHangUpEventMessage;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.FreeswitchApplication;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
@ -53,12 +56,39 @@ public class RedisMessageReceiver {
|
||||
case StopRecordingVoiceConfRequestMessage.STOP_RECORD_VOICE_CONF_REQUEST:
|
||||
processStopRecordingVoiceConfRequestMessage(message);
|
||||
break;
|
||||
case DeskShareStartRTMPBroadcastEventMessage.DESKSHARE_START_RTMP_BROADCAST_MESSAGE:
|
||||
System.out.println("RedisMessageReceiver got DESKSHARE_START_RTMP_BROADCAST_MESSAGE");
|
||||
processDeskShareStartRTMPBroadcastEventMessage(message);
|
||||
break;
|
||||
case DeskShareStopRTMPBroadcastEventMessage.DESKSHARE_STOP_RTMP_BROADCAST_MESSAGE:
|
||||
System.out.println("RedisMessageReceiver got DESKSHARE_STOP_RTMP_BROADCAST_MESSAGE");
|
||||
processDeskShareStopRTMPBroadcastEventMessage(message);
|
||||
break;
|
||||
case DeskShareHangUpEventMessage.DESKSHARE_HANG_UP_MESSAGE:
|
||||
System.out.println("RedisMessageReceiver got DESKSHARE_HANG_UP_MESSAGE");
|
||||
processDeskShareHangUpEventMessage(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processDeskShareStartRTMPBroadcastEventMessage(String json) {
|
||||
DeskShareStartRTMPBroadcastEventMessage msg = DeskShareStartRTMPBroadcastEventMessage.fromJson(json);
|
||||
fsApp.deskShareBroadcastRTMP(msg.conferenceName, msg.streamUrl, msg.timestamp, true);
|
||||
}
|
||||
|
||||
private void processDeskShareStopRTMPBroadcastEventMessage(String json) {
|
||||
DeskShareStopRTMPBroadcastEventMessage msg = DeskShareStopRTMPBroadcastEventMessage.fromJson(json);
|
||||
fsApp.deskShareBroadcastRTMP(msg.conferenceName, msg.streamUrl, msg.timestamp, false);
|
||||
}
|
||||
|
||||
private void processDeskShareHangUpEventMessage(String json) {
|
||||
DeskShareHangUpEventMessage msg = DeskShareHangUpEventMessage.fromJson(json);
|
||||
fsApp.deskShareHangUp(msg.conferenceName, msg.fsConferenceName, msg.timestamp);
|
||||
}
|
||||
|
||||
private void processEjectAllVoiceUsersRequestMessage(String json) {
|
||||
EjectAllUsersFromVoiceConfRequestMessage msg = EjectAllUsersFromVoiceConfRequestMessage.fromJson(json);
|
||||
fsApp.ejectAll(msg.voiceConfId);
|
||||
|
@ -24,7 +24,10 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareStartedEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareEndedEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareRTMPBroadcastEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceConferenceEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceStartRecordingEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceUserJoinedEvent;
|
||||
@ -78,13 +81,33 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
|
||||
VoiceStartRecordingEvent evt = (VoiceStartRecordingEvent) event;
|
||||
System.out.println("************** FreeswitchConferenceEventListener VoiceStartRecordingEvent recording=[" + evt.startRecord() + "]");
|
||||
vcs.voiceConfRecordingStarted(evt.getRoom(), evt.getRecordingFilename(), evt.startRecord(), evt.getTimestamp());
|
||||
}
|
||||
} else if (event instanceof DeskShareStartedEvent) {
|
||||
DeskShareStartedEvent evt = (DeskShareStartedEvent) event;
|
||||
System.out.println("************** FreeswitchConferenceEventListener DeskShareStartedEvent");
|
||||
vcs.deskShareStarted(evt.getRoom(), evt.getCallerIdNum(), evt.getCallerIdName());
|
||||
} else if (event instanceof DeskShareEndedEvent) {
|
||||
DeskShareEndedEvent evt = (DeskShareEndedEvent) event;
|
||||
System.out.println("************** FreeswitchConferenceEventListener DeskShareEndedEvent");
|
||||
vcs.deskShareEnded(evt.getRoom(), evt.getCallerIdNum(), evt.getCallerIdName());
|
||||
} else if (event instanceof DeskShareRTMPBroadcastEvent) {
|
||||
if (((DeskShareRTMPBroadcastEvent) event).getBroadcast()) {
|
||||
DeskShareRTMPBroadcastEvent evt = (DeskShareRTMPBroadcastEvent) event;
|
||||
System.out.println("************** FreeswitchConferenceEventListener DeskShareRTMPBroadcastStartedEvent");
|
||||
vcs.deskShareRTMPBroadcastStarted(evt.getRoom(), evt.getBroadcastingStreamUrl(),
|
||||
evt.getVideoWidth(), evt.getVideoHeight(), evt.getTimestamp());
|
||||
} else {
|
||||
DeskShareRTMPBroadcastEvent evt = (DeskShareRTMPBroadcastEvent) event;
|
||||
System.out.println("************** FreeswitchConferenceEventListener DeskShareRTMPBroadcastStoppedEvent");
|
||||
vcs.deskShareRTMPBroadcastStopped(evt.getRoom(), evt.getBroadcastingStreamUrl(),
|
||||
evt.getVideoWidth(), evt.getVideoHeight(), evt.getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
runExec.execute(task);
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
sendMessages = true;
|
||||
Runnable sender = new Runnable() {
|
||||
@ -97,7 +120,7 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -111,5 +134,5 @@ public class FreeswitchConferenceEventListener implements ConferenceEventListene
|
||||
public void handleConferenceEvent(VoiceConferenceEvent event) {
|
||||
queueMessage(event);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -8,5 +8,9 @@ public interface IVoiceConferenceService {
|
||||
void userLockedInVoiceConf(String voiceConfId, String voiceUserId, Boolean locked);
|
||||
void userMutedInVoiceConf(String voiceConfId, String voiceUserId, Boolean muted);
|
||||
void userTalkingInVoiceConf(String voiceConfId, String voiceUserId, Boolean talking);
|
||||
void deskShareStarted(String voiceConfId, String callerIdNum, String callerIdName);
|
||||
void deskShareEnded(String voiceConfId, String callerIdNum, String callerIdName);
|
||||
void deskShareRTMPBroadcastStarted(String room, String streamname, Integer videoWidth, Integer videoHeight, String timestamp);
|
||||
void deskShareRTMPBroadcastStopped(String room, String streamname, Integer videoWidth, Integer videoHeight, String timestamp);
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.freeswitch.voice.events;
|
||||
|
||||
public class DeskShareEndedEvent extends VoiceConferenceEvent {
|
||||
|
||||
private final String callerIdNum;
|
||||
private final String callerIdName;
|
||||
|
||||
public DeskShareEndedEvent(String room, String callerIdNum, String callerIdName) {
|
||||
super(room);
|
||||
this.callerIdName = callerIdName;
|
||||
this.callerIdNum = callerIdNum;
|
||||
}
|
||||
|
||||
public String getCallerIdNum() {
|
||||
return callerIdNum;
|
||||
}
|
||||
|
||||
public String getCallerIdName() {
|
||||
return callerIdName;
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
/**
|
||||
* 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.freeswitch.voice.events;
|
||||
|
||||
public class DeskShareRTMPBroadcastEvent extends VoiceConferenceEvent {
|
||||
|
||||
private String timestamp;
|
||||
private boolean broadcast;
|
||||
private String streamUrl;
|
||||
private Integer vw;
|
||||
private Integer vh;
|
||||
|
||||
private final String DESKSHARE_SUFFIX = "-DESKSHARE";
|
||||
|
||||
|
||||
public DeskShareRTMPBroadcastEvent(String room, boolean broadcast) {
|
||||
super(room);
|
||||
this.broadcast = broadcast;
|
||||
}
|
||||
|
||||
public void setTimestamp(String timestamp) {
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public void setBroadcastingStreamUrl(String streamUrl) {
|
||||
this.streamUrl = streamUrl;
|
||||
}
|
||||
|
||||
public void setVideoWidth(Integer vw) {this.vw = vw;}
|
||||
|
||||
public void setVideoHeight(Integer vh) {this.vh = vh;}
|
||||
|
||||
public Integer getVideoHeight() {return vh;}
|
||||
|
||||
public Integer getVideoWidth() {return vw;}
|
||||
|
||||
public String getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public String getBroadcastingStreamUrl()
|
||||
{
|
||||
if (streamUrl.endsWith(DESKSHARE_SUFFIX)) {
|
||||
streamUrl = streamUrl.replace(DESKSHARE_SUFFIX, "");
|
||||
}
|
||||
return streamUrl;
|
||||
}
|
||||
|
||||
public boolean getBroadcast() {
|
||||
return broadcast;
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/**
|
||||
* 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.freeswitch.voice.events;
|
||||
|
||||
public class DeskShareStartedEvent extends VoiceConferenceEvent {
|
||||
|
||||
private final String callerIdNum;
|
||||
private final String callerIdName;
|
||||
|
||||
public DeskShareStartedEvent(String room, String callerIdNum, String callerIdName) {
|
||||
super(room);
|
||||
this.callerIdName = callerIdName;
|
||||
this.callerIdNum = callerIdNum;
|
||||
}
|
||||
|
||||
public String getCallerIdNum() {
|
||||
return callerIdNum;
|
||||
}
|
||||
|
||||
public String getCallerIdName() {
|
||||
return callerIdName;
|
||||
}
|
||||
}
|
@ -24,12 +24,7 @@ import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
|
||||
import org.freeswitch.esl.client.inbound.Client;
|
||||
import org.freeswitch.esl.client.inbound.InboundConnectionFailure;
|
||||
import org.freeswitch.esl.client.manager.ManagerConnection;
|
||||
@ -143,5 +138,23 @@ public class ConnectionManager {
|
||||
EslMessage response = c.sendSyncApiCommand(rcc.getCommand(), rcc.getCommandArgs());
|
||||
rcc.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void broadcastRTMP(DeskShareBroadcastRTMPCommand rtmp) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
System.out.println("ConnectionManager: send to FS: broadcastRTMP " + rtmp.getCommandArgs());
|
||||
EslMessage response = c.sendSyncApiCommand(rtmp.getCommand(), rtmp.getCommandArgs());
|
||||
rtmp.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
|
||||
public void hangUp(DeskShareHangUpCommand huCmd) {
|
||||
Client c = manager.getESLClient();
|
||||
if (c.canSend()) {
|
||||
System.out.println("ConnectionManager: send to FS: hangUp " + huCmd.getCommandArgs());
|
||||
EslMessage response = c.sendSyncApiCommand(huCmd.getCommand(), huCmd.getCommandArgs());
|
||||
huCmd.handleResponse(response, conferenceEventListener);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,10 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareEndedEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareStartedEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.DeskShareRTMPBroadcastEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceConferenceEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceStartRecordingEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceUserJoinedEvent;
|
||||
import org.bigbluebutton.freeswitch.voice.events.VoiceUserLeftEvent;
|
||||
@ -22,11 +26,15 @@ public class ESLEventListener implements IEslEventListener {
|
||||
private static final String STOP_TALKING_EVENT = "stop-talking";
|
||||
private static final String START_RECORDING_EVENT = "start-recording";
|
||||
private static final String STOP_RECORDING_EVENT = "stop-recording";
|
||||
|
||||
|
||||
private static final String DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE";
|
||||
private static final String DESKSHARE_CALLER_NAME_SUFFIX = " (Screen)";
|
||||
private static final String DESKSHARE_CALLER_ID_SUFFIX = " (screen)";
|
||||
|
||||
private final ConferenceEventListener conferenceEventListener;
|
||||
|
||||
public ESLEventListener(ConferenceEventListener conferenceEventListener) {
|
||||
this.conferenceEventListener = conferenceEventListener;
|
||||
this.conferenceEventListener = conferenceEventListener;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,7 +58,7 @@ public class ESLEventListener implements IEslEventListener {
|
||||
|
||||
@Override
|
||||
public void conferenceEventJoin(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
|
||||
|
||||
Integer memberId = this.getMemberIdFromEvent(event);
|
||||
Map<String, String> headers = event.getEventHeaders();
|
||||
String callerId = this.getCallerIdFromEvent(event);
|
||||
@ -59,29 +67,52 @@ public class ESLEventListener implements IEslEventListener {
|
||||
boolean speaking = headers.get("Talking").equals("true") ? true : false;
|
||||
|
||||
String voiceUserId = callerIdName;
|
||||
|
||||
|
||||
System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" + confName + "]");
|
||||
|
||||
|
||||
Matcher gapMatcher = GLOBAL_AUDION_PATTERN.matcher(callerIdName);
|
||||
if (gapMatcher.matches()) {
|
||||
System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]");
|
||||
return;
|
||||
System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
|
||||
if (matcher.matches()) {
|
||||
voiceUserId = matcher.group(1).trim();
|
||||
callerIdName = matcher.group(2).trim();
|
||||
}
|
||||
|
||||
VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, null);
|
||||
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
|
||||
// Deskstop sharing conferences have the user with the desktop video displayed in this way:
|
||||
// username (Screen) and usernum (screen)
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
|
||||
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
|
||||
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
|
||||
DeskShareStartedEvent dsStart = new DeskShareStartedEvent(confName, callerId, callerIdName);
|
||||
conferenceEventListener.handleConferenceEvent(dsStart);
|
||||
}
|
||||
|
||||
Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
|
||||
if (matcher.matches()) {
|
||||
voiceUserId = matcher.group(1).trim();
|
||||
callerIdName = matcher.group(2).trim();
|
||||
}
|
||||
|
||||
VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
|
||||
conferenceEventListener.handleConferenceEvent(pj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
Integer memberId = this.getMemberIdFromEvent(event);
|
||||
System.out.println("User left voice conference, user=[" + memberId.toString() + "], conf=[" + confName + "]");
|
||||
String callerId = this.getCallerIdFromEvent(event);
|
||||
String callerIdName = this.getCallerIdNameFromEvent(event);
|
||||
|
||||
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
|
||||
// Deskstop sharing conferences have the user with the desktop video displayed in this way:
|
||||
// username (Screen) and usernum (screen)
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
|
||||
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
|
||||
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
|
||||
DeskShareEndedEvent dsEnd = new DeskShareEndedEvent(confName, callerId, callerIdName);
|
||||
conferenceEventListener.handleConferenceEvent(dsEnd);
|
||||
}
|
||||
|
||||
VoiceUserLeftEvent pl = new VoiceUserLeftEvent(memberId.toString(), confName);
|
||||
conferenceEventListener.handleConferenceEvent(pl);
|
||||
}
|
||||
@ -117,12 +148,12 @@ public class ESLEventListener implements IEslEventListener {
|
||||
|
||||
if (action.equals(START_TALKING_EVENT)) {
|
||||
pt = new VoiceUserTalkingEvent(memberId.toString(), confName, true);
|
||||
conferenceEventListener.handleConferenceEvent(pt);
|
||||
conferenceEventListener.handleConferenceEvent(pt);
|
||||
} else if (action.equals(STOP_TALKING_EVENT)) {
|
||||
pt = new VoiceUserTalkingEvent(memberId.toString(), confName, false);
|
||||
conferenceEventListener.handleConferenceEvent(pt);
|
||||
conferenceEventListener.handleConferenceEvent(pt);
|
||||
} else {
|
||||
System.out.println("Unknown conference Action [" + action + "]");
|
||||
System.out.println("Unknown conference Action [" + action + "]");
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,52 +164,80 @@ public class ESLEventListener implements IEslEventListener {
|
||||
|
||||
@Override
|
||||
public void conferenceEventThreadRun(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
|
||||
|
||||
}
|
||||
|
||||
//@Override
|
||||
public void conferenceEventRecord(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
String action = event.getEventHeaders().get("Action");
|
||||
|
||||
if(action == null) {
|
||||
String action = event.getEventHeaders().get("Action");
|
||||
|
||||
if(action == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println("Handling conferenceEventRecord " + action);
|
||||
|
||||
if (action.equals(START_RECORDING_EVENT)) {
|
||||
VoiceStartRecordingEvent sre = new VoiceStartRecordingEvent(confName, true);
|
||||
sre.setRecordingFilename(getRecordFilenameFromEvent(event));
|
||||
sre.setTimestamp(genTimestamp().toString());
|
||||
|
||||
System.out.println("Voice conference recording started. file=[" + getRecordFilenameFromEvent(event) + "], conf=[" + confName + "]");
|
||||
|
||||
conferenceEventListener.handleConferenceEvent(sre);
|
||||
} else if (action.equals(STOP_RECORDING_EVENT)) {
|
||||
VoiceStartRecordingEvent srev = new VoiceStartRecordingEvent(confName, false);
|
||||
srev.setRecordingFilename(getRecordFilenameFromEvent(event));
|
||||
srev.setTimestamp(genTimestamp().toString());
|
||||
|
||||
System.out.println("Voice conference recording stopped. file=[" + getRecordFilenameFromEvent(event) + "], conf=[" + confName + "]");
|
||||
conferenceEventListener.handleConferenceEvent(srev);
|
||||
} else {
|
||||
System.out.println("Processing UNKNOWN conference Action " + action + "]");
|
||||
}
|
||||
|
||||
if (action.equals(START_RECORDING_EVENT)) {
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)){
|
||||
if (isRTMPStream(event)) {
|
||||
DeskShareRTMPBroadcastEvent rtmp = new DeskShareRTMPBroadcastEvent(confName, true);
|
||||
rtmp.setBroadcastingStreamUrl(getStreamUrl(event));
|
||||
rtmp.setVideoHeight(Integer.parseInt(getBroadcastParameter(event, "vh")));
|
||||
rtmp.setVideoWidth(Integer.parseInt(getBroadcastParameter(event, "vw")));
|
||||
rtmp.setTimestamp(genTimestamp().toString());
|
||||
|
||||
System.out.println("DeskShare conference broadcast started. url=["
|
||||
+ getStreamUrl(event) + "], conf=[" + confName + "]");
|
||||
conferenceEventListener.handleConferenceEvent(rtmp);
|
||||
}
|
||||
} else {
|
||||
VoiceStartRecordingEvent sre = new VoiceStartRecordingEvent(confName, true);
|
||||
sre.setRecordingFilename(getRecordFilenameFromEvent(event));
|
||||
sre.setTimestamp(genTimestamp().toString());
|
||||
|
||||
System.out.println("Voice conference recording started. file=["
|
||||
+ getRecordFilenameFromEvent(event) + "], conf=[" + confName + "]");
|
||||
conferenceEventListener.handleConferenceEvent(sre);
|
||||
}
|
||||
} else if (action.equals(STOP_RECORDING_EVENT)) {
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)){
|
||||
if (isRTMPStream(event)) {
|
||||
DeskShareRTMPBroadcastEvent rtmp = new DeskShareRTMPBroadcastEvent(confName, false);
|
||||
rtmp.setBroadcastingStreamUrl(getStreamUrl(event));
|
||||
rtmp.setVideoHeight(Integer.parseInt(getBroadcastParameter(event, "vh")));
|
||||
rtmp.setVideoWidth(Integer.parseInt(getBroadcastParameter(event, "vw")));
|
||||
rtmp.setTimestamp(genTimestamp().toString());
|
||||
|
||||
System.out.println("DeskShare conference broadcast stopped. url=["
|
||||
+ getStreamUrl(event) + "], conf=[" + confName + "]");
|
||||
conferenceEventListener.handleConferenceEvent(rtmp);
|
||||
}
|
||||
} else {
|
||||
VoiceStartRecordingEvent sre = new VoiceStartRecordingEvent(confName, false);
|
||||
sre.setRecordingFilename(getRecordFilenameFromEvent(event));
|
||||
sre.setTimestamp(genTimestamp().toString());
|
||||
System.out.println("Voice conference recording stopped. file=["
|
||||
+ getRecordFilenameFromEvent(event) + "], conf=[" + confName + "]");
|
||||
conferenceEventListener.handleConferenceEvent(sre);
|
||||
}
|
||||
}
|
||||
|
||||
else {
|
||||
System.out.println("Processing UNKNOWN conference Action " + action + "]");
|
||||
}
|
||||
}
|
||||
|
||||
private Long genTimestamp() {
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventReceived(EslEvent event) {
|
||||
System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]");
|
||||
@Override
|
||||
public void eventReceived(EslEvent event) {
|
||||
System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]");
|
||||
// if (event.getEventName().equals(FreeswitchHeartbeatMonitor.EVENT_HEARTBEAT)) {
|
||||
//// setChanged();
|
||||
// notifyObservers(event);
|
||||
// return;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private Integer getMemberIdFromEvent(EslEvent e) {
|
||||
return new Integer(e.getEventHeaders().get("Member-ID"));
|
||||
@ -191,8 +250,52 @@ public class ESLEventListener implements IEslEventListener {
|
||||
private String getCallerIdNameFromEvent(EslEvent e) {
|
||||
return e.getEventHeaders().get("Caller-Caller-ID-Name");
|
||||
}
|
||||
|
||||
|
||||
private String getRecordFilenameFromEvent(EslEvent e) {
|
||||
return e.getEventHeaders().get("Path");
|
||||
return e.getEventHeaders().get("Path");
|
||||
}
|
||||
|
||||
// Distinguish between recording to a file:
|
||||
// /path/to/a/file.mp4
|
||||
// and broadcasting a stream:
|
||||
// {channels=2,samplerate=48000,vw=1920,vh=1080,fps=15.00}rtmp://192.168.0.109/live/abc/dev-test
|
||||
private Boolean isRTMPStream(EslEvent e) {
|
||||
String path = e.getEventHeaders().get("Path");
|
||||
|
||||
if (path.contains("rtmp") && path.contains("channels")
|
||||
&& path.contains("samplerate") && path.contains("vw")
|
||||
&& path.contains("vh") && path.contains("fps")) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// returns a String so that we can parse to an int or double depending on the param
|
||||
private String getBroadcastParameter(EslEvent e, String param) {
|
||||
String path = e.getEventHeaders().get("Path");
|
||||
if (isRTMPStream(e)) {
|
||||
String temp = path.substring(path.indexOf("{") + 1, path.indexOf("}"));
|
||||
String[] arr = temp.split(",");
|
||||
for (int i = 0; i < 5; i++) {
|
||||
if (arr[i].startsWith(param)) {
|
||||
return arr[i].substring(arr[i].indexOf('=') + 1);
|
||||
}
|
||||
}
|
||||
return "0";
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain the rtmp url from the event (if any):
|
||||
private String getStreamUrl(EslEvent e) {
|
||||
String path = e.getEventHeaders().get("Path");
|
||||
if (isRTMPStream(e)){
|
||||
return path.substring(path.lastIndexOf("}") + 1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/**
|
||||
* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
|
||||
*
|
||||
* Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
|
||||
* 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
|
||||
@ -24,77 +24,81 @@ import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.BroadcastConferenceCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.EjectUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.FreeswitchCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.MuteUserCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.GetAllUsersCommand;
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.RecordConferenceCommand;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.freeswitch.actions.*;
|
||||
|
||||
public class FreeswitchApplication {
|
||||
|
||||
|
||||
private static final int SENDERTHREADS = 1;
|
||||
private static final Executor msgSenderExec = Executors.newFixedThreadPool(SENDERTHREADS);
|
||||
private static final Executor runExec = Executors.newFixedThreadPool(SENDERTHREADS);
|
||||
private BlockingQueue<FreeswitchCommand> messages = new LinkedBlockingQueue<FreeswitchCommand>();
|
||||
|
||||
|
||||
private final ConnectionManager manager;
|
||||
|
||||
|
||||
private final String USER = "0"; /* not used for now */
|
||||
|
||||
|
||||
private volatile boolean sendMessages = false;
|
||||
|
||||
|
||||
public FreeswitchApplication(ConnectionManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
private void queueMessage(FreeswitchCommand command) {
|
||||
try {
|
||||
messages.offer(command, 5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void getAllUsers(String voiceConfId) {
|
||||
GetAllUsersCommand prc = new GetAllUsersCommand(voiceConfId, USER);
|
||||
queueMessage(prc);
|
||||
}
|
||||
|
||||
public void muteUser(String voiceConfId, String voiceUserId, Boolean mute) {
|
||||
MuteUserCommand mpc = new MuteUserCommand(voiceConfId, voiceUserId, mute, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
public void eject(String voiceConfId, String voiceUserId) {
|
||||
EjectUserCommand mpc = new EjectUserCommand(voiceConfId, voiceUserId, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
public void ejectAll(String voiceConfId) {
|
||||
EjectAllUsersCommand mpc = new EjectAllUsersCommand(voiceConfId, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
private Long genTimestamp() {
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
}
|
||||
|
||||
public void startRecording(String voiceConfId, String meetingid){
|
||||
String RECORD_DIR = "/var/freeswitch/meetings";
|
||||
String voicePath = RECORD_DIR + File.separatorChar + meetingid + "-" + genTimestamp() + ".wav";
|
||||
|
||||
RecordConferenceCommand rcc = new RecordConferenceCommand(voiceConfId, USER, true, voicePath);
|
||||
queueMessage(rcc);
|
||||
}
|
||||
|
||||
public void stopRecording(String voiceConfId, String meetingid, String voicePath){
|
||||
RecordConferenceCommand rcc = new RecordConferenceCommand(voiceConfId, USER, false, voicePath);
|
||||
queueMessage(rcc);
|
||||
}
|
||||
|
||||
private void queueMessage(FreeswitchCommand command) {
|
||||
try {
|
||||
messages.offer(command, 5, TimeUnit.SECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void getAllUsers(String voiceConfId) {
|
||||
GetAllUsersCommand prc = new GetAllUsersCommand(voiceConfId, USER);
|
||||
queueMessage(prc);
|
||||
}
|
||||
|
||||
public void muteUser(String voiceConfId, String voiceUserId, Boolean mute) {
|
||||
MuteUserCommand mpc = new MuteUserCommand(voiceConfId, voiceUserId, mute, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
public void eject(String voiceConfId, String voiceUserId) {
|
||||
EjectUserCommand mpc = new EjectUserCommand(voiceConfId, voiceUserId, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
public void ejectAll(String voiceConfId) {
|
||||
EjectAllUsersCommand mpc = new EjectAllUsersCommand(voiceConfId, USER);
|
||||
queueMessage(mpc);
|
||||
}
|
||||
|
||||
private Long genTimestamp() {
|
||||
return TimeUnit.NANOSECONDS.toMillis(System.nanoTime());
|
||||
}
|
||||
|
||||
public void startRecording(String voiceConfId, String meetingid){
|
||||
String RECORD_DIR = "/var/freeswitch/meetings";
|
||||
String voicePath = RECORD_DIR + File.separatorChar + meetingid + "-" + genTimestamp() + ".wav";
|
||||
|
||||
RecordConferenceCommand rcc = new RecordConferenceCommand(voiceConfId, USER, true, voicePath);
|
||||
queueMessage(rcc);
|
||||
}
|
||||
|
||||
public void stopRecording(String voiceConfId, String meetingid, String voicePath){
|
||||
RecordConferenceCommand rcc = new RecordConferenceCommand(voiceConfId, USER, false, voicePath);
|
||||
queueMessage(rcc);
|
||||
}
|
||||
|
||||
public void deskShareBroadcastRTMP(String voiceConfId, String streamUrl, String timestamp, Boolean broadcast){
|
||||
DeskShareBroadcastRTMPCommand rtmp = new DeskShareBroadcastRTMPCommand(voiceConfId, USER, streamUrl, timestamp, broadcast);
|
||||
queueMessage(rtmp);
|
||||
}
|
||||
|
||||
public void deskShareHangUp(String voiceConfId, String fsConferenceName, String timestamp){
|
||||
DeskShareHangUpCommand huCmd = new DeskShareHangUpCommand(voiceConfId, fsConferenceName, USER, timestamp);
|
||||
queueMessage(huCmd);
|
||||
}
|
||||
private void sendMessageToFreeswitch(final FreeswitchCommand command) {
|
||||
Runnable task = new Runnable() {
|
||||
public void run() {
|
||||
@ -105,7 +109,6 @@ public class FreeswitchApplication {
|
||||
} else if (command instanceof MuteUserCommand) {
|
||||
MuteUserCommand cmd = (MuteUserCommand) command;
|
||||
System.out.println("Sending MuteParticipantCommand for conference = [" + cmd.getRoom() + "]");
|
||||
System.out.println("Sending MuteParticipantCommand for conference = [" + cmd.getRoom() + "]");
|
||||
manager.mute(cmd);
|
||||
} else if (command instanceof EjectUserCommand) {
|
||||
EjectUserCommand cmd = (EjectUserCommand) command;
|
||||
@ -117,15 +120,20 @@ public class FreeswitchApplication {
|
||||
manager.ejectAll(cmd);
|
||||
} else if (command instanceof RecordConferenceCommand) {
|
||||
manager.record((RecordConferenceCommand) command);
|
||||
} else if (command instanceof DeskShareBroadcastRTMPCommand) {
|
||||
manager.broadcastRTMP((DeskShareBroadcastRTMPCommand)command);
|
||||
} else if (command instanceof DeskShareHangUpCommand) {
|
||||
DeskShareHangUpCommand cmd = (DeskShareHangUpCommand) command;
|
||||
manager.hangUp(cmd);
|
||||
} else if (command instanceof BroadcastConferenceCommand) {
|
||||
manager.broadcast((BroadcastConferenceCommand) command);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
runExec.execute(task);
|
||||
|
||||
runExec.execute(task);
|
||||
}
|
||||
|
||||
|
||||
public void start() {
|
||||
sendMessages = true;
|
||||
Runnable sender = new Runnable() {
|
||||
@ -134,18 +142,17 @@ public class FreeswitchApplication {
|
||||
FreeswitchCommand message;
|
||||
try {
|
||||
message = messages.take();
|
||||
sendMessageToFreeswitch(message);
|
||||
sendMessageToFreeswitch(message);
|
||||
} catch (InterruptedException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
};
|
||||
msgSenderExec.execute(sender);
|
||||
msgSenderExec.execute(sender);
|
||||
}
|
||||
|
||||
|
||||
public void stop() {
|
||||
sendMessages = false;
|
||||
}
|
||||
|
@ -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.freeswitch.voice.freeswitch.actions;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.freeswitch.esl.client.transport.message.EslMessage;
|
||||
|
||||
public class DeskShareBroadcastRTMPCommand extends FreeswitchCommand {
|
||||
|
||||
private String broadcastPath;
|
||||
private boolean broadcast;
|
||||
private String timestamp;
|
||||
private final String DESKSHARE_SUFFIX = "-DESKSHARE";
|
||||
|
||||
public DeskShareBroadcastRTMPCommand(String room, String requesterId, String broadcastPath, String timestamp, boolean broadcast){
|
||||
super(room, requesterId);
|
||||
this.broadcastPath = broadcastPath;
|
||||
this.broadcast = broadcast;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCommandArgs() {
|
||||
String action = "norecord";
|
||||
if (broadcast) {
|
||||
action = "record";
|
||||
}
|
||||
|
||||
String room = getRoom();
|
||||
if (!room.endsWith(DESKSHARE_SUFFIX)) {
|
||||
room = room + DESKSHARE_SUFFIX;
|
||||
}
|
||||
|
||||
return SPACE + room + SPACE + action + SPACE + broadcastPath;
|
||||
}
|
||||
|
||||
public void handleResponse(EslMessage response, ConferenceEventListener eventListener) {
|
||||
//Test for Known Conference
|
||||
System.out.println("\nDeskShareBroadcastRTMPCommand\n");
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
package org.bigbluebutton.freeswitch.voice.freeswitch.actions;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.freeswitch.esl.client.transport.message.EslMessage;
|
||||
|
||||
/**
|
||||
* Created by anton on 07/01/16.
|
||||
*/
|
||||
public class DeskShareHangUpCommand extends FreeswitchCommand {
|
||||
private String timestamp;
|
||||
private String fsConferenceName;
|
||||
private final String DESKSHARE_SUFFIX = "-DESKSHARE";
|
||||
|
||||
public DeskShareHangUpCommand(String room, String fsConferenceName, String requesterId, String timestamp){
|
||||
super(room, requesterId);
|
||||
this.timestamp = timestamp;
|
||||
this.fsConferenceName = fsConferenceName;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCommandArgs() {
|
||||
String action = "kick all";
|
||||
|
||||
if(!fsConferenceName.endsWith(DESKSHARE_SUFFIX)) {
|
||||
fsConferenceName = fsConferenceName + DESKSHARE_SUFFIX;
|
||||
}
|
||||
return SPACE + fsConferenceName + SPACE + action;
|
||||
}
|
||||
|
||||
public void handleResponse(EslMessage response, ConferenceEventListener eventListener) {
|
||||
System.out.println("\nDeskShareHangUpCommand\n");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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.freeswitch.voice.freeswitch.actions;
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.events.ConferenceEventListener;
|
||||
import org.freeswitch.esl.client.transport.message.EslMessage;
|
||||
|
||||
public class DeskShareRecordCommand extends FreeswitchCommand {
|
||||
|
||||
private String recordPath;
|
||||
private boolean record;
|
||||
|
||||
public DeskShareRecordCommand(String room, String requesterId, boolean record, String recordPath){
|
||||
super(room, requesterId);
|
||||
this.recordPath = recordPath;
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getCommandArgs() {
|
||||
String action = "norecord";
|
||||
if (record)
|
||||
action = "record";
|
||||
|
||||
System.out.println("\n\n\n\n\n DESKSHARE RECORD " + record + "\n\n\n\n");
|
||||
return SPACE + getRoom() + SPACE + action + SPACE + recordPath;
|
||||
}
|
||||
|
||||
public void handleResponse(EslMessage response, ConferenceEventListener eventListener) {
|
||||
//Test for Known Conference
|
||||
System.out.println("\n\n\n\n\nLALALALLALA\n\n\n\n");
|
||||
}
|
||||
}
|
@ -2,11 +2,20 @@ package org.bigbluebutton.freeswitch
|
||||
|
||||
import org.bigbluebutton.freeswitch.voice.IVoiceConferenceService
|
||||
import org.bigbluebutton.endpoint.redis.RedisPublisher
|
||||
import org.bigbluebutton.common.messages._
|
||||
import org.bigbluebutton.common.messages.VoiceConfRecordingStartedMessage
|
||||
import org.bigbluebutton.common.messages.UserJoinedVoiceConfMessage
|
||||
import org.bigbluebutton.common.messages.UserLeftVoiceConfMessage
|
||||
import org.bigbluebutton.common.messages.UserMutedInVoiceConfMessage
|
||||
import org.bigbluebutton.common.messages.UserTalkingInVoiceConfMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareStartedEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareStoppedEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStartedEventMessage
|
||||
import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStoppedEventMessage
|
||||
|
||||
class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceService {
|
||||
|
||||
val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system";
|
||||
private final val DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE"
|
||||
|
||||
def voiceConfRecordingStarted(voiceConfId: String, recordStream: String, recording: java.lang.Boolean, timestamp: String) {
|
||||
val msg = new VoiceConfRecordingStartedMessage(voiceConfId, recordStream, recording, timestamp)
|
||||
@ -41,4 +50,33 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer
|
||||
val msg = new UserTalkingInVoiceConfMessage(voiceConfId, voiceUserId, talking)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareStarted(voiceConfId: String, callerIdNum: String, callerIdName: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareStarted to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareStartedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareEnded(voiceConfId: String, callerIdNum: String, callerIdName: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareStopped to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareStoppedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStarted(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStartedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStopped(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStoppedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,10 +1,9 @@
|
||||
|
||||
|
||||
name := "bbb-common-message"
|
||||
|
||||
organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.17-SNAPSHOT"
|
||||
version := "0.0.17"
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
// This way we'll have the right path when we import
|
||||
@ -48,19 +47,19 @@ autoScalaLibrary := false
|
||||
* publish to the local maven repo using "sbt publish"
|
||||
*/
|
||||
// Uncomment this to publish to local maven repo while commenting out the nexus repo
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
//publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
|
||||
|
||||
// Comment this out when publishing to local maven repo using SNAPSHOT version.
|
||||
// To push to sonatype "sbt publishSigned"
|
||||
|
||||
// publishTo := {
|
||||
// val nexus = "https://oss.sonatype.org/"
|
||||
// if (isSnapshot.value)
|
||||
// Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
// else
|
||||
// Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
// }
|
||||
publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
if (isSnapshot.value)
|
||||
Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
else
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
}
|
||||
|
||||
|
||||
// Enables publishing to maven repo
|
||||
|
@ -32,7 +32,7 @@ public class FromJsonDecoder {
|
||||
StartCustomPollRequestMessage msg = gson.fromJson(message, StartCustomPollRequestMessage.class);
|
||||
return msg;
|
||||
} else {
|
||||
System.out.println("Unknown message name=[" + messageName + "]");
|
||||
// System.out.println("Unknown message name=[" + messageName + "]");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -126,4 +126,10 @@ public class Constants {
|
||||
public static final String PERM_LOCK_ON_JOIN_CONFIG = "lockOnJoinConfigurable";
|
||||
public static final String ENABLED = "enabled";
|
||||
public static final String AVATAR_URL = "avatarURL";
|
||||
public static final String STUNS = "stuns";
|
||||
public static final String TURNS = "turns";
|
||||
public static final String USERNAME = "username";
|
||||
public static final String URL = "url";
|
||||
public static final String TTL = "ttl";
|
||||
public static final String PASSWORD = "password";
|
||||
}
|
||||
|
@ -0,0 +1,61 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
/**
|
||||
* Created by anton on 21/12/15.
|
||||
*/
|
||||
public class DeskShareGetInfoRequestMessage {
|
||||
public static final String GET_DESKTOP_SHARE_GET_INFO_REQUEST = "desktop_share_get_info_request";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingId;
|
||||
public final String requesterId;
|
||||
public final String replyTo;
|
||||
|
||||
|
||||
public DeskShareGetInfoRequestMessage(String meetingId, String requesterId, String replyTo) {
|
||||
this.meetingId = meetingId;
|
||||
this.requesterId = requesterId;
|
||||
this.replyTo = replyTo;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingId);
|
||||
payload.put(Constants.REQUESTER_ID, requesterId);
|
||||
payload.put(Constants.REPLY_TO, replyTo);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(GET_DESKTOP_SHARE_GET_INFO_REQUEST, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareGetInfoRequestMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (GET_DESKTOP_SHARE_GET_INFO_REQUEST.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.REPLY_TO)
|
||||
&& payload.has(Constants.REQUESTER_ID)) {
|
||||
String id = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String requesterId = payload.get(Constants.REQUESTER_ID).getAsString();
|
||||
String replyTo = payload.get(Constants.REPLY_TO).getAsString();
|
||||
return new DeskShareGetInfoRequestMessage(id, requesterId, replyTo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by anton on 07/01/16.
|
||||
*/
|
||||
|
||||
// akka-bbb-apps to akka-bbb-fsesl
|
||||
public class DeskShareHangUpEventMessage {
|
||||
public static final String DESKSHARE_HANG_UP_MESSAGE = "deskshare_hang_up_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String FS_CONFERENCE_NAME = "fs_conference_name";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String timestamp;
|
||||
public final String fsConferenceName;
|
||||
|
||||
public DeskShareHangUpEventMessage(String conferenceName, String fsConferenceName, String timestamp) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.fsConferenceName = fsConferenceName;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(FS_CONFERENCE_NAME, fsConferenceName);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_HANG_UP_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareHangUpEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_HANG_UP_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(FS_CONFERENCE_NAME)
|
||||
&& payload.has(TIMESTAMP)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String fsConferenceName = payload.get(FS_CONFERENCE_NAME).getAsString();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
|
||||
return new DeskShareHangUpEventMessage(conferenceName, fsConferenceName, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,93 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
//Message from bbb-akka-apps to bigbluebutton-apps (in red5)
|
||||
public class DeskShareNotifyASingleViewerEventMessage {
|
||||
|
||||
|
||||
|
||||
public static final String DESK_SHARE_NOTIFY_A_SINGLE_VIEWER = "desk_share_notify_a_single_viewer";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String MEETING_ID = "meeting_id";
|
||||
public static final String USER_ID = "userid";
|
||||
public static final String STREAM_PATH = "stream_path";
|
||||
public static final String BROADCASTING = "broadcasting";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String VIDEO_WIDTH = "vw";
|
||||
public static final String VIDEO_HEIGHT = "vh";
|
||||
|
||||
public final String meetingId;
|
||||
public final String userId;
|
||||
public final String streamPath;
|
||||
public final boolean broadcasting;
|
||||
public final String timestamp;
|
||||
public final int vw;
|
||||
public final int vh;
|
||||
|
||||
public DeskShareNotifyASingleViewerEventMessage(String meetingId, String userId, String streamPath,
|
||||
boolean broadcasting, int vw, int vh, String timestamp) {
|
||||
this.meetingId = meetingId;
|
||||
this.userId=userId;
|
||||
this.streamPath = streamPath;
|
||||
this.broadcasting = broadcasting;
|
||||
this.timestamp = timestamp;
|
||||
this.vw = vw;
|
||||
this.vh = vh;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(MEETING_ID, meetingId);
|
||||
payload.put(USER_ID, userId);
|
||||
payload.put(STREAM_PATH, streamPath);
|
||||
payload.put(BROADCASTING, broadcasting);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(VIDEO_HEIGHT, vh);
|
||||
payload.put(VIDEO_WIDTH, vw);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESK_SHARE_NOTIFY_A_SINGLE_VIEWER, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareNotifyASingleViewerEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESK_SHARE_NOTIFY_A_SINGLE_VIEWER.equals(messageName)) {
|
||||
if (payload.has(MEETING_ID)
|
||||
&& payload.has(USER_ID)
|
||||
&& payload.has(BROADCASTING)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(VIDEO_HEIGHT)
|
||||
&& payload.has(VIDEO_WIDTH)
|
||||
&& payload.has(STREAM_PATH)) {
|
||||
String meetingId = payload.get(MEETING_ID).getAsString();
|
||||
String userId = payload.get(USER_ID).getAsString();
|
||||
String streamPath = payload.get(STREAM_PATH).getAsString();
|
||||
boolean broadcasting = payload.get(BROADCASTING).getAsBoolean();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
int vh = payload.get(VIDEO_HEIGHT).getAsInt();
|
||||
int vw = payload.get(VIDEO_WIDTH).getAsInt();
|
||||
|
||||
return new DeskShareNotifyASingleViewerEventMessage(meetingId, userId, streamPath,
|
||||
broadcasting, vw, vh, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
//Message from bbb-akka-apps to bbb-apps-red5
|
||||
public class DeskShareNotifyViewersRTMPEventMessage {
|
||||
public static final String DESK_SHARE_NOTIFY_VIEWERS_RTMP = "desk_share_notify_viewers_rtmp";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String MEETING_ID = "meeting_id";
|
||||
public static final String STREAM_PATH = "stream_path";
|
||||
public static final String BROADCASTING = "broadcasting";
|
||||
public static final String VIDEO_WIDTH = "vw";
|
||||
public static final String VIDEO_HEIGHT = "vh";
|
||||
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public final String meetingId;
|
||||
public final String streamPath;
|
||||
public final boolean broadcasting;
|
||||
public final String timestamp;
|
||||
public final int vw;
|
||||
public final int vh;
|
||||
|
||||
public DeskShareNotifyViewersRTMPEventMessage(String meetingId, String streamPath,
|
||||
boolean broadcasting, int vw, int vh, String timestamp) {
|
||||
this.meetingId = meetingId;
|
||||
this.streamPath = streamPath;
|
||||
this.broadcasting = broadcasting;
|
||||
this.timestamp = timestamp;
|
||||
this.vw = vw;
|
||||
this.vh = vh;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(MEETING_ID, meetingId);
|
||||
payload.put(STREAM_PATH, streamPath);
|
||||
payload.put(BROADCASTING, broadcasting);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(VIDEO_HEIGHT, vh);
|
||||
payload.put(VIDEO_WIDTH, vw);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESK_SHARE_NOTIFY_VIEWERS_RTMP, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareNotifyViewersRTMPEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESK_SHARE_NOTIFY_VIEWERS_RTMP.equals(messageName)) {
|
||||
if (payload.has(MEETING_ID)
|
||||
&& payload.has(BROADCASTING)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(VIDEO_HEIGHT)
|
||||
&& payload.has(VIDEO_WIDTH)
|
||||
&& payload.has(STREAM_PATH)) {
|
||||
String meetingId = payload.get(MEETING_ID).getAsString();
|
||||
String streamPath = payload.get(STREAM_PATH).getAsString();
|
||||
boolean broadcasting = payload.get(BROADCASTING).getAsBoolean();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
int vh = payload.get(VIDEO_HEIGHT).getAsInt();
|
||||
int vw = payload.get(VIDEO_WIDTH).getAsInt();
|
||||
|
||||
return new DeskShareNotifyViewersRTMPEventMessage(meetingId, streamPath, broadcasting, vw, vh, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class DeskShareRTMPBroadcastStartedEventMessage {
|
||||
public static final String DESKSHARE_RTMP_BROADCAST_STARTED_MESSAGE = "deskshare_rtmp_broadcast_started_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String STREAMNAME = "streamname";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String VIDEO_WIDTH = "vw";
|
||||
public static final String VIDEO_HEIGHT = "vh";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String streamname;
|
||||
public final String timestamp;
|
||||
public final int vw;
|
||||
public final int vh;
|
||||
|
||||
public DeskShareRTMPBroadcastStartedEventMessage(String conferenceName, String streamname, int vw, int vh, String timestamp) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.streamname = streamname;
|
||||
this.timestamp = timestamp;
|
||||
this.vw = vw;
|
||||
this.vh = vh;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(STREAMNAME, streamname);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(VIDEO_HEIGHT, vh);
|
||||
payload.put(VIDEO_WIDTH, vw);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_RTMP_BROADCAST_STARTED_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareRTMPBroadcastStartedEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_RTMP_BROADCAST_STARTED_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(VIDEO_HEIGHT)
|
||||
&& payload.has(VIDEO_WIDTH)
|
||||
&& payload.has(STREAMNAME)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String streamname = payload.get(STREAMNAME).getAsString();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
int vh = payload.get(VIDEO_HEIGHT).getAsInt();
|
||||
int vw = payload.get(VIDEO_WIDTH).getAsInt();
|
||||
|
||||
return new DeskShareRTMPBroadcastStartedEventMessage(conferenceName, streamname, vw, vh, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class DeskShareRTMPBroadcastStoppedEventMessage {
|
||||
public static final String DESKSHARE_RTMP_BROADCAST_STOPPED_MESSAGE = "deskshare_rtmp_broadcast_stopped_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String STREAMNAME = "streamname";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String VIDEO_WIDTH = "vw";
|
||||
public static final String VIDEO_HEIGHT = "vh";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String streamname;
|
||||
public final String timestamp;
|
||||
public final int vw;
|
||||
public final int vh;
|
||||
|
||||
public DeskShareRTMPBroadcastStoppedEventMessage(String conferenceName, String streamname, int vw, int vh, String timestamp) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.streamname = streamname;
|
||||
this.timestamp = timestamp;
|
||||
this.vw = vw;
|
||||
this.vh = vh;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(STREAMNAME, streamname);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(VIDEO_HEIGHT, vh);
|
||||
payload.put(VIDEO_WIDTH, vw);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_RTMP_BROADCAST_STOPPED_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareRTMPBroadcastStoppedEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_RTMP_BROADCAST_STOPPED_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(VIDEO_HEIGHT)
|
||||
&& payload.has(VIDEO_WIDTH)
|
||||
&& payload.has(STREAMNAME)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String streamname = payload.get(STREAMNAME).getAsString();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
int vh = payload.get(VIDEO_HEIGHT).getAsInt();
|
||||
int vw = payload.get(VIDEO_WIDTH).getAsInt();
|
||||
|
||||
return new DeskShareRTMPBroadcastStoppedEventMessage(conferenceName, streamname, vw, vh, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class DeskShareStartRTMPBroadcastEventMessage {
|
||||
public static final String DESKSHARE_START_RTMP_BROADCAST_MESSAGE = "deskshare_start_rtmp_broadcast_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String STREAMURL = "stream_url";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String streamUrl;
|
||||
public final String timestamp;
|
||||
|
||||
public DeskShareStartRTMPBroadcastEventMessage(String conferenceName, String streamUrl, String timestamp) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.streamUrl = streamUrl;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(STREAMURL, streamUrl);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_START_RTMP_BROADCAST_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareStartRTMPBroadcastEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_START_RTMP_BROADCAST_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(STREAMURL)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String streamUrl = payload.get(STREAMURL).getAsString();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
|
||||
return new DeskShareStartRTMPBroadcastEventMessage(conferenceName, streamUrl, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
//Message from FreeSwitch to bbb-akka-apps
|
||||
public class DeskShareStartedEventMessage {
|
||||
public static final String DESKSHARE_STARTED_MESSAGE = "deskshare_start_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String CALLER_ID = "caller_id";
|
||||
public static final String CALLER_ID_NAME = "caller_id_name";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String callerId;
|
||||
public final String callerIdName;
|
||||
|
||||
public DeskShareStartedEventMessage(String conferenceName, String callerId, String callerIdName) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.callerId = callerId;
|
||||
this.callerIdName = callerIdName;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(CALLER_ID_NAME, callerIdName);
|
||||
payload.put(CALLER_ID, callerId);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_STARTED_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareStartedEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_STARTED_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(CALLER_ID)
|
||||
&& payload.has(CALLER_ID_NAME)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String callerId = payload.get(CALLER_ID_NAME).getAsString();
|
||||
String callerIdName = payload.get(CALLER_ID_NAME).getAsString();
|
||||
|
||||
return new DeskShareStartedEventMessage(conferenceName, callerId, callerIdName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
|
||||
public class DeskShareStopRTMPBroadcastEventMessage {
|
||||
public static final String DESKSHARE_STOP_RTMP_BROADCAST_MESSAGE = "deskshare_stop_rtmp_broadcast_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String STREAMURL = "stream_url";
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String streamUrl;
|
||||
public final String timestamp;
|
||||
|
||||
public DeskShareStopRTMPBroadcastEventMessage(String conferenceName, String streamUrl, String timestamp) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.streamUrl = streamUrl;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(STREAMURL, streamUrl);
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESKSHARE_STOP_RTMP_BROADCAST_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareStopRTMPBroadcastEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESKSHARE_STOP_RTMP_BROADCAST_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(TIMESTAMP)
|
||||
&& payload.has(STREAMURL)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String streamUrl = payload.get(STREAMURL).getAsString();
|
||||
String timestamp = payload.get(TIMESTAMP).getAsString();
|
||||
|
||||
return new DeskShareStopRTMPBroadcastEventMessage(conferenceName, streamUrl, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
//Message from FreeSwitch to bbb-akka-apps
|
||||
public class DeskShareStoppedEventMessage {
|
||||
public static final String DESK_SHARE_STOPPED_MESSAGE = "desk_share_stopped_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String CONFERENCE_NAME = "conference_name";
|
||||
public static final String CALLER_ID = "caller_id";
|
||||
public static final String CALLER_ID_NAME = "caller_id_name";
|
||||
|
||||
public final String conferenceName;
|
||||
public final String callerId;
|
||||
public final String callerIdName;
|
||||
|
||||
public DeskShareStoppedEventMessage(String conferenceName, String callerId, String callerIdName) {
|
||||
this.conferenceName = conferenceName;
|
||||
this.callerId = callerId;
|
||||
this.callerIdName = callerIdName;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(CONFERENCE_NAME, conferenceName);
|
||||
payload.put(CALLER_ID_NAME, callerIdName);
|
||||
payload.put(CALLER_ID, callerId);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(DESK_SHARE_STOPPED_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static DeskShareStoppedEventMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (DESK_SHARE_STOPPED_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(CONFERENCE_NAME)
|
||||
&& payload.has(CALLER_ID)
|
||||
&& payload.has(CALLER_ID_NAME)) {
|
||||
String conferenceName = payload.get(CONFERENCE_NAME).getAsString();
|
||||
String callerId = payload.get(CALLER_ID_NAME).getAsString();
|
||||
String callerIdName = payload.get(CALLER_ID_NAME).getAsString();
|
||||
|
||||
return new DeskShareStoppedEventMessage(conferenceName, callerId, callerIdName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
@ -29,6 +29,7 @@ public class MessagingConstants {
|
||||
public static final String FROM_USERS_CHANNEL = FROM_BBB_APPS_CHANNEL + ":users";
|
||||
public static final String FROM_CHAT_CHANNEL = FROM_BBB_APPS_CHANNEL + ":chat";
|
||||
public static final String FROM_WHITEBOARD_CHANNEL = FROM_BBB_APPS_CHANNEL + ":whiteboard";
|
||||
public static final String FROM_DESK_SHARE_CHANNEL = FROM_BBB_APPS_CHANNEL + ":deskshare";
|
||||
|
||||
public static final String TO_BBB_APPS_CHANNEL = "bigbluebutton:to-bbb-apps";
|
||||
public static final String TO_BBB_APPS_PATTERN = TO_BBB_APPS_CHANNEL + ":*";
|
||||
@ -42,7 +43,9 @@ public class MessagingConstants {
|
||||
public static final String TO_WHITEBOARD_CHANNEL = TO_BBB_APPS_CHANNEL + ":whiteboard";
|
||||
|
||||
public static final String BBB_APPS_KEEP_ALIVE_CHANNEL = "bigbluebutton:from-bbb-apps:keepalive";
|
||||
|
||||
|
||||
public static final String TO_BBB_HTML5_CHANNEL = "bigbluebutton:to-bbb-html5";
|
||||
|
||||
public static final String TO_VOICE_CONF_CHANNEL = "bigbluebutton:to-voice-conf";
|
||||
public static final String TO_VOICE_CONF_PATTERN = TO_VOICE_CONF_CHANNEL + ":*";
|
||||
public static final String TO_VOICE_CONF_SYSTEM_CHAN = TO_VOICE_CONF_CHANNEL + ":system";
|
||||
|
@ -0,0 +1,74 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SendStunTurnInfoReplyMessage {
|
||||
public static final String SEND_STUN_TURN_INFO_REPLY_MESSAGE = "send_stun_turn_info_reply_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingId;
|
||||
public final String requesterId;
|
||||
public final ArrayList<String> stuns;
|
||||
public final ArrayList<Map<String, Object>> turns;
|
||||
|
||||
public SendStunTurnInfoReplyMessage(String meetingId, String requesterId, ArrayList<String> stuns,
|
||||
ArrayList<Map<String, Object>> turns) {
|
||||
this.meetingId = meetingId;
|
||||
this.requesterId = requesterId;
|
||||
this.stuns = stuns;
|
||||
this.turns = turns;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingId);
|
||||
payload.put(Constants.REQUESTER_ID, requesterId);
|
||||
payload.put(Constants.STUNS, stuns);
|
||||
payload.put(Constants.TURNS, turns);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_STUN_TURN_INFO_REPLY_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static SendStunTurnInfoReplyMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (SEND_STUN_TURN_INFO_REPLY_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.STUNS)
|
||||
&& payload.has(Constants.TURNS)
|
||||
&& payload.has(Constants.REQUESTER_ID)) {
|
||||
String meetingId = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String requesterId = payload.get(Constants.REQUESTER_ID).getAsString();
|
||||
|
||||
Util util = new Util();
|
||||
JsonArray stunsArray = (JsonArray) payload.get(Constants.STUNS);
|
||||
ArrayList<String> stunsArrayList = util.extractStuns(stunsArray);
|
||||
|
||||
JsonArray turnsArray = (JsonArray) payload.get(Constants.TURNS);
|
||||
ArrayList<Map<String, Object>> turnsArrayList = util.extractTurns(turnsArray);
|
||||
|
||||
return new SendStunTurnInfoReplyMessage(meetingId, requesterId, stunsArrayList, turnsArrayList);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Created by anton on 05/02/16.
|
||||
*/
|
||||
public class SendStunTurnInfoRequestMessage {
|
||||
public static final String SEND_STUN_TURN_INFO_REQUEST_MESSAGE = "send_stun_turn_info_request_message";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingId;
|
||||
public final String requesterId;
|
||||
|
||||
|
||||
public SendStunTurnInfoRequestMessage(String meetingId, String requesterId) {
|
||||
this.meetingId = meetingId;
|
||||
this.requesterId = requesterId;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingId);
|
||||
payload.put(Constants.REQUESTER_ID, requesterId);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(SEND_STUN_TURN_INFO_REQUEST_MESSAGE, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static SendStunTurnInfoRequestMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (SEND_STUN_TURN_INFO_REQUEST_MESSAGE.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)
|
||||
&& payload.has(Constants.REQUESTER_ID)) {
|
||||
String meetingId = payload.get(Constants.MEETING_ID).getAsString();
|
||||
String requesterId = payload.get(Constants.REQUESTER_ID).getAsString();
|
||||
return new SendStunTurnInfoRequestMessage(meetingId, requesterId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -119,6 +119,52 @@ public class Util {
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<String> extractStuns(JsonArray stunsArray) {
|
||||
ArrayList<String> collection = new ArrayList<String>();
|
||||
Iterator<JsonElement> stunIter = stunsArray.iterator();
|
||||
while (stunIter.hasNext()) {
|
||||
JsonElement aStun = stunIter.next();
|
||||
if (aStun != null) {
|
||||
collection.add(aStun.getAsString());
|
||||
}
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<Map<String, Object>> extractTurns(JsonArray turnsArray) {
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
Iterator<JsonElement> turnIter = turnsArray.iterator();
|
||||
while (turnIter.hasNext()){
|
||||
JsonElement aTurn = turnIter.next();
|
||||
Map<String, Object> turnMap = extractATurn((JsonObject)aTurn);
|
||||
if (turnMap != null) {
|
||||
collection.add(turnMap);
|
||||
}
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
private Map<String,Object> extractATurn(JsonObject aTurn) {
|
||||
if (aTurn.has(Constants.USERNAME)
|
||||
&& aTurn.has(Constants.TTL)
|
||||
&& aTurn.has(Constants.URL)
|
||||
&& aTurn.has(Constants.PASSWORD)) {
|
||||
|
||||
Map<String, Object> turnMap = new HashMap<String, Object>();
|
||||
|
||||
turnMap.put(Constants.USERNAME, aTurn.get(Constants.USERNAME).getAsString());
|
||||
turnMap.put(Constants.URL, aTurn.get(Constants.URL).getAsString());
|
||||
turnMap.put(Constants.PASSWORD, aTurn.get(Constants.PASSWORD).getAsString());
|
||||
turnMap.put(Constants.TTL, aTurn.get(Constants.TTL).getAsInt());
|
||||
|
||||
return turnMap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ArrayList<Map<String, Object>> extractChatHistory(JsonArray history) {
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
Iterator<JsonElement> historyIter = history.iterator();
|
||||
@ -163,7 +209,7 @@ public class Util {
|
||||
|
||||
public ArrayList<Map<String, Object>> extractUsers(JsonArray users) {
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
|
||||
|
||||
Iterator<JsonElement> usersIter = users.iterator();
|
||||
while (usersIter.hasNext()){
|
||||
JsonElement user = usersIter.next();
|
||||
@ -172,39 +218,39 @@ public class Util {
|
||||
collection.add(userMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return collection;
|
||||
|
||||
|
||||
}
|
||||
|
||||
public ArrayList<String> extractWebcamStreams(JsonArray webcamStreams) {
|
||||
ArrayList<String> collection = new ArrayList<String>();
|
||||
|
||||
|
||||
Iterator<JsonElement> webcamStreamsIter = webcamStreams.iterator();
|
||||
while (webcamStreamsIter.hasNext()){
|
||||
JsonElement stream = webcamStreamsIter.next();
|
||||
collection.add(stream.getAsString());
|
||||
}
|
||||
|
||||
|
||||
return collection;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public ArrayList<String> extractUserids(JsonArray users) {
|
||||
ArrayList<String> collection = new ArrayList<String>();
|
||||
|
||||
|
||||
Iterator<JsonElement> usersIter = users.iterator();
|
||||
while (usersIter.hasNext()){
|
||||
JsonElement user = usersIter.next();
|
||||
collection.add(user.getAsString());
|
||||
}
|
||||
|
||||
|
||||
return collection;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Map<String, Object> extractAnnotation(JsonObject annotationElement) {
|
||||
//NON-TEXT SHAPE
|
||||
if (annotationElement.has(Constants.ID)
|
||||
@ -300,11 +346,11 @@ public class Util {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> extractCurrentPresenter(JsonObject vu) {
|
||||
if (vu.has(Constants.USER_ID) && vu.has(Constants.NAME)
|
||||
&& vu.has(Constants.ASSIGNED_BY)){
|
||||
|
||||
|
||||
Map<String, Object> vuMap = new HashMap<String, Object>();
|
||||
String presenterUserId = vu.get(Constants.USER_ID).getAsString();
|
||||
String presenterName = vu.get(Constants.NAME).getAsString();
|
||||
@ -313,41 +359,41 @@ public class Util {
|
||||
vuMap.put("userId", presenterUserId);
|
||||
vuMap.put("name", presenterName);
|
||||
vuMap.put("assignedBy", assignedBy);
|
||||
|
||||
|
||||
return vuMap;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public ArrayList<Map<String, Object>> extractPresentationPages(JsonArray pagesArray) {
|
||||
ArrayList<Map<String, Object>> pages = new ArrayList<Map<String, Object>>();
|
||||
|
||||
|
||||
Iterator<JsonElement> pagesIter = pagesArray.iterator();
|
||||
while (pagesIter.hasNext()){
|
||||
JsonObject pageObj = (JsonObject)pagesIter.next();
|
||||
if (pageObj.has("id") && pageObj.has("num")
|
||||
if (pageObj.has("id") && pageObj.has("num")
|
||||
&& pageObj.has("thumb_uri") && pageObj.has("swf_uri")
|
||||
&& pageObj.has("txt_uri") && pageObj.has("svg_uri")
|
||||
&& pageObj.has("current") && pageObj.has("x_offset")
|
||||
&& pageObj.has("y_offset") && pageObj.has("width_ratio")
|
||||
&& pageObj.has("height_ratio")) {
|
||||
|
||||
|
||||
Map<String, Object> page = new HashMap<String, Object>();
|
||||
|
||||
|
||||
String pageId = pageObj.get("id").getAsString();
|
||||
Integer pageNum = pageObj.get("num").getAsInt();
|
||||
String pageThumbUri = pageObj.get("thumb_uri").getAsString();
|
||||
String pageSwfUri = pageObj.get("swf_uri").getAsString();
|
||||
String pageTxtUri = pageObj.get("txt_uri").getAsString();
|
||||
String pageSvgUri = pageObj.get("svg_uri").getAsString();
|
||||
|
||||
|
||||
Boolean currentPage = pageObj.get("current").getAsBoolean();
|
||||
Double xOffset = pageObj.get("x_offset").getAsDouble();
|
||||
Double yOffset = pageObj.get("y_offset").getAsDouble();
|
||||
Double widthRatio = pageObj.get("width_ratio").getAsDouble();
|
||||
Double heightRatio = pageObj.get("height_ratio").getAsDouble();
|
||||
|
||||
|
||||
page.put("id", pageId);
|
||||
page.put("num", pageNum);
|
||||
page.put("thumbUri", pageThumbUri);
|
||||
@ -359,7 +405,7 @@ public class Util {
|
||||
page.put("yOffset", yOffset);
|
||||
page.put("widthRatio", widthRatio);
|
||||
page.put("heightRatio", heightRatio);
|
||||
|
||||
|
||||
pages.add(page);
|
||||
}
|
||||
}
|
||||
@ -368,7 +414,7 @@ public class Util {
|
||||
|
||||
public ArrayList<Map<String, Object>> extractPresentations(JsonArray presArray) {
|
||||
ArrayList<Map<String, Object>> presentations = new ArrayList<Map<String, Object>>();
|
||||
|
||||
|
||||
Iterator<JsonElement> presentationsIter = presArray.iterator();
|
||||
while (presentationsIter.hasNext()){
|
||||
JsonObject presObj = (JsonObject)presentationsIter.next();
|
||||
@ -378,7 +424,7 @@ public class Util {
|
||||
}
|
||||
|
||||
public Map<String, Object> extractPresentation(JsonObject presObj) {
|
||||
if (presObj.has(Constants.ID) && presObj.has(Constants.NAME)
|
||||
if (presObj.has(Constants.ID) && presObj.has(Constants.NAME)
|
||||
&& presObj.has(Constants.CURRENT) && presObj.has(Constants.PAGES)) {
|
||||
Map<String, Object> pres = new HashMap<String, Object>();
|
||||
|
||||
@ -393,7 +439,7 @@ public class Util {
|
||||
JsonArray pagesJsonArray = presObj.get(Constants.PAGES).getAsJsonArray();
|
||||
|
||||
ArrayList<Map<String, Object>> pages = extractPresentationPages(pagesJsonArray);
|
||||
// store the pages in the presentation
|
||||
// store the pages in the presentation
|
||||
pres.put(Constants.PAGES, pages);
|
||||
|
||||
// add this presentation into our presentations list
|
||||
@ -417,20 +463,20 @@ public class Util {
|
||||
}
|
||||
return collection;
|
||||
}
|
||||
|
||||
|
||||
public Map<String, Object> extractPollResultAnnotation(JsonObject annotationElement) {
|
||||
if (annotationElement.has("result") && annotationElement.has("whiteboardId")
|
||||
&& annotationElement.has("points")) {
|
||||
Map<String, Object> finalAnnotation = new HashMap<String, Object>();
|
||||
|
||||
|
||||
String whiteboardId = annotationElement.get("whiteboardId").getAsString();
|
||||
Integer numRespondents = annotationElement.get(NUM_RESPONDENTS).getAsInt();
|
||||
Integer numResponders = annotationElement.get(NUM_RESPONDERS).getAsInt();
|
||||
|
||||
|
||||
String resultJson = annotationElement.get("result").getAsString();
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonArray resultJsonArray = parser.parse(resultJson).getAsJsonArray();
|
||||
|
||||
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
Iterator<JsonElement> resultIter = resultJsonArray.iterator();
|
||||
|
||||
@ -443,10 +489,10 @@ public class Util {
|
||||
vote.put("id", vid);
|
||||
vote.put("num_votes", vvotes);
|
||||
vote.put("key", vkey);
|
||||
|
||||
|
||||
collection.add(vote);
|
||||
}
|
||||
|
||||
|
||||
JsonArray pointsJsonArray = annotationElement.get("points").getAsJsonArray();
|
||||
ArrayList<Float> pointsArray = new ArrayList<Float>();
|
||||
Iterator<JsonElement> pointIter = pointsJsonArray.iterator();
|
||||
@ -457,13 +503,13 @@ public class Util {
|
||||
pointsArray.add(pf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
finalAnnotation.put("whiteboardId", whiteboardId);
|
||||
finalAnnotation.put(NUM_RESPONDENTS, numRespondents);
|
||||
finalAnnotation.put(NUM_RESPONDERS, numResponders);
|
||||
finalAnnotation.put("result", collection);
|
||||
finalAnnotation.put("points", pointsArray);
|
||||
|
||||
|
||||
return finalAnnotation;
|
||||
}
|
||||
return null;
|
||||
@ -488,13 +534,13 @@ public class Util {
|
||||
|
||||
JsonElement shape = annotationElement.get("shape");
|
||||
Map<String, Object> shapesMap;
|
||||
|
||||
|
||||
if (type.equals("poll_result")) {
|
||||
shapesMap = extractPollResultAnnotation((JsonObject)shape);
|
||||
} else {
|
||||
shapesMap = extractAnnotation((JsonObject)shape);
|
||||
}
|
||||
|
||||
|
||||
if (shapesMap != null) {
|
||||
finalAnnotation.put("shapes", shapesMap);
|
||||
}
|
||||
@ -582,30 +628,30 @@ public class Util {
|
||||
if (answer.has(Constants.ID) && answer.has(KEY)) {
|
||||
String id = answer.get(Constants.ID).getAsString();
|
||||
String key = answer.get(KEY).getAsString();
|
||||
|
||||
|
||||
answerMap.put(Constants.ID, id);
|
||||
answerMap.put(KEY, key);
|
||||
answerMap.put(KEY, key);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return answerMap;
|
||||
}
|
||||
|
||||
|
||||
public static final String ANSWERS = "answers";
|
||||
public static final String KEY = "key";
|
||||
public static final String NUM_VOTES = "num_votes";
|
||||
public static final String NUM_RESPONDERS = "num_responders";
|
||||
public static final String NUM_RESPONDENTS = "num_respondents";
|
||||
|
||||
|
||||
public Map<String, Object> decodeSimplePoll(JsonObject poll) {
|
||||
Map<String, Object> pollMap = new HashMap<String, Object>();
|
||||
|
||||
|
||||
if (poll.has(Constants.ID) && poll.has(ANSWERS)) {
|
||||
String id = poll.get(Constants.ID).getAsString();
|
||||
String id = poll.get(Constants.ID).getAsString();
|
||||
JsonArray answers = poll.get(ANSWERS).getAsJsonArray();
|
||||
|
||||
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
|
||||
|
||||
Iterator<JsonElement> answersIter = answers.iterator();
|
||||
while (answersIter.hasNext()){
|
||||
JsonElement qElem = answersIter.next();
|
||||
@ -616,12 +662,12 @@ public class Util {
|
||||
collection.add(answerMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pollMap.put(Constants.ID, id);
|
||||
pollMap.put(ANSWERS, collection);
|
||||
pollMap.put(ANSWERS, collection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return pollMap;
|
||||
}
|
||||
|
||||
@ -631,27 +677,26 @@ public class Util {
|
||||
String id = answer.get(Constants.ID).getAsString();
|
||||
String key = answer.get(KEY).getAsString();
|
||||
Integer numVotes = answer.get(NUM_VOTES).getAsInt();
|
||||
|
||||
|
||||
answerMap.put(Constants.ID, id);
|
||||
answerMap.put(KEY, key);
|
||||
answerMap.put(KEY, key);
|
||||
answerMap.put(NUM_VOTES, numVotes);
|
||||
}
|
||||
|
||||
return answerMap;
|
||||
}
|
||||
|
||||
public Map<String, Object> decodeSimplePollResult(JsonObject poll) {
|
||||
Map<String, Object> pollMap = new HashMap<String, Object>();
|
||||
|
||||
|
||||
if (poll.has(Constants.ID) && poll.has(ANSWERS)) {
|
||||
String id = poll.get(Constants.ID).getAsString();
|
||||
String id = poll.get(Constants.ID).getAsString();
|
||||
Integer numRespondents = poll.get(NUM_RESPONDENTS).getAsInt();
|
||||
Integer numResponders = poll.get(NUM_RESPONDERS).getAsInt();
|
||||
|
||||
|
||||
JsonArray answers = poll.get(ANSWERS).getAsJsonArray();
|
||||
|
||||
|
||||
ArrayList<Map<String, Object>> collection = new ArrayList<Map<String, Object>>();
|
||||
|
||||
|
||||
Iterator<JsonElement> answersIter = answers.iterator();
|
||||
while (answersIter.hasNext()){
|
||||
JsonElement qElem = answersIter.next();
|
||||
@ -662,16 +707,14 @@ public class Util {
|
||||
collection.add(answerMap);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pollMap.put(Constants.ID, id);
|
||||
pollMap.put(NUM_RESPONDENTS, numRespondents);
|
||||
pollMap.put(NUM_RESPONDERS, numResponders);
|
||||
pollMap.put(ANSWERS, collection);
|
||||
pollMap.put(ANSWERS, collection);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return pollMap;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,14 @@
|
||||
package org.bigbluebutton.core.service.whiteboard;
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
public class WhiteboardKeyUtil {
|
||||
|
||||
public static final String TEXT_TYPE = "text";
|
||||
public static final String PENCIL_TYPE = "pencil";
|
||||
public static final String RECTANGLE_TYPE = "rectangle";
|
||||
public static final String ELLIPSE_TYPE = "ellipse";
|
||||
public static final String TRIANGLE_TYPE = "triangle";
|
||||
public static final String LINE_TYPE = "line";
|
||||
public static final String POLL_RESULT_TYPE = "poll_result";
|
||||
|
||||
public static final String TEXT_CREATED_STATUS = "textCreated";
|
||||
public static final String DRAW_START_STATUS = "DRAW_START";
|
||||
public static final String DRAW_END_STATUS = "DRAW_END";
|
||||
|
||||
public static final String POLL_RESULT_TYPE = "poll_result";
|
||||
}
|
@ -6,7 +6,7 @@ description := "BigBlueButton custom FS-ESL client built on top of FS-ESL Java l
|
||||
|
||||
organization := "org.bigbluebutton"
|
||||
|
||||
version := "0.0.3"
|
||||
version := "0.0.4"
|
||||
|
||||
// We want to have our jar files in lib_managed dir.
|
||||
// This way we'll have the right path when we import
|
||||
@ -44,18 +44,15 @@ crossPaths := false
|
||||
// This forbids including Scala related libraries into the dependency
|
||||
autoScalaLibrary := false
|
||||
|
||||
//publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository")))
|
||||
|
||||
|
||||
publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/dev/repo/maven-repo/releases" )) )
|
||||
|
||||
publishTo := {
|
||||
val nexus = "https://oss.sonatype.org/"
|
||||
if (isSnapshot.value)
|
||||
Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
else
|
||||
Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
}
|
||||
//publishTo := {
|
||||
// val nexus = "https://oss.sonatype.org/"
|
||||
// if (isSnapshot.value)
|
||||
// Some("snapshots" at nexus + "content/repositories/snapshots")
|
||||
// else
|
||||
// Some("releases" at nexus + "service/local/staging/deploy/maven2")
|
||||
//}
|
||||
|
||||
|
||||
// Enables publishing to maven repo
|
||||
@ -80,4 +77,4 @@ pomExtra := (
|
||||
|
||||
licenses := Seq("Apache License, Version 2.0" -> url("http://opensource.org/licenses/Apache-2.0"))
|
||||
|
||||
homepage := Some(url("http://www.bigbluebutton.org"))
|
||||
homepage := Some(url("http://www.bigbluebutton.org"))
|
||||
|
@ -475,26 +475,21 @@ public class Client
|
||||
System.out.println("##### Client member_add_file_data");
|
||||
listener.conferenceEventPlayFile(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
//API has changed between freeswitch 1.4 and 1.6
|
||||
} else if (eventFunc.equals("conf_api_sub_transfer") || eventFunc.equals("conference_api_sub_transfer")) {
|
||||
//Member transfered to another conf...
|
||||
listener.conferenceEventTransfer(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
//API has changed between freeswitch 1.4 and 1.6
|
||||
} else if (eventFunc.equals("conference_add_member") || eventFunc.equals("conference_member_add")) {
|
||||
System.out.println("##### Client conference_add_member");
|
||||
listener.conferenceEventJoin(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
//API has changed between freeswitch 1.4 and 1.6
|
||||
} else if (eventFunc.equals("conference_del_member") || eventFunc.equals("conference_member_del")) {
|
||||
System.out.println("##### Client conference_del_member");
|
||||
listener.conferenceEventLeave(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
//API has changed between freeswitch 1.4 and 1.6
|
||||
} else if (eventFunc.equals("conf_api_sub_mute") || eventFunc.equals("conference_api_sub_mute")) {
|
||||
listener.conferenceEventMute(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
//API has changed between freeswitch 1.4 and 1.6
|
||||
} else if (eventFunc.equals("conf_api_sub_unmute") || eventFunc.equals("conference_api_sub_unmute")) {
|
||||
listener.conferenceEventUnMute(uniqueId, confName, confSize, event);
|
||||
return;
|
||||
|
@ -179,7 +179,11 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
String meetingId = conn.getScope().getName();
|
||||
String streamId = stream.getPublishedName();
|
||||
|
||||
publisher.userSharedWebcamMessage(meetingId, userId, streamId);
|
||||
//publisher.userSharedWebcamMessage(meetingId, userId, streamId);
|
||||
log.info("^^^^^^^^^^^publisher.userSharedWebcamMessage(meetingId, userId, streamId);");
|
||||
|
||||
|
||||
|
||||
VideoStreamListener listener = new VideoStreamListener(conn.getScope(), stream, recordVideoStream, userId, packetTimeout);
|
||||
listener.setEventRecordingService(recordingService);
|
||||
stream.addStreamListener(listener);
|
||||
@ -212,7 +216,10 @@ public class VideoApplication extends MultiThreadedApplicationAdapter {
|
||||
String meetingId = conn.getScope().getName();
|
||||
String streamId = stream.getPublishedName();
|
||||
|
||||
publisher.userUnshareWebcamRequestMessage(meetingId, userId, streamId);
|
||||
//publisher.userUnshareWebcamRequestMessage(meetingId, userId, streamId);
|
||||
log.info("^^^^^^^^^^^publisher.userUnshareWebcamRequestMessage(meetingId, userId, streamId);");
|
||||
|
||||
|
||||
|
||||
IStreamListener listener = streamListeners.remove(scopeName + "-" + stream.getPublishedName());
|
||||
if (listener != null) {
|
||||
|
@ -101,10 +101,10 @@ dependencies {
|
||||
compile "redis.clients:jedis:2.7.2"
|
||||
compile 'org.apache.commons:commons-pool2:2.3'
|
||||
|
||||
compile 'com.google.code.gson:gson:1.7.1'
|
||||
providedCompile 'org.apache.commons:commons-lang3:3.2'
|
||||
compile 'com.google.code.gson:gson:1.7.1'
|
||||
providedCompile 'org.apache.commons:commons-lang3:3.2'
|
||||
|
||||
compile 'org.bigbluebutton:bbb-common-message:0.0.17-SNAPSHOT'
|
||||
compile 'org.bigbluebutton:bbb-common-message:0.0.17'
|
||||
}
|
||||
|
||||
test {
|
||||
|
@ -0,0 +1,85 @@
|
||||
package org.bigbluebutton.red5.client;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bigbluebutton.common.messages.ChatKeyUtil;
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyViewersRTMPEventMessage;
|
||||
import org.bigbluebutton.common.messages.DeskShareNotifyASingleViewerEventMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.BroadcastClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.DirectClientMessage;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class DeskShareMessageSender {
|
||||
private ConnectionInvokerService service;
|
||||
|
||||
public DeskShareMessageSender(ConnectionInvokerService service) {
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
public void handleDeskShareMessage(String message) {
|
||||
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
switch (messageName) {
|
||||
case DeskShareNotifyViewersRTMPEventMessage.DESK_SHARE_NOTIFY_VIEWERS_RTMP:
|
||||
DeskShareNotifyViewersRTMPEventMessage rtmp = DeskShareNotifyViewersRTMPEventMessage.fromJson(message);
|
||||
|
||||
if (rtmp != null) {
|
||||
processDeskShareNotifyViewersRTMPEventMessage(rtmp);
|
||||
}
|
||||
break;
|
||||
case DeskShareNotifyASingleViewerEventMessage.DESK_SHARE_NOTIFY_A_SINGLE_VIEWER:
|
||||
DeskShareNotifyASingleViewerEventMessage singleViewerMsg = DeskShareNotifyASingleViewerEventMessage.fromJson(message);
|
||||
if (singleViewerMsg != null) {
|
||||
processDeskShareNotifyASingleViewerEventMessage(singleViewerMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processDeskShareNotifyViewersRTMPEventMessage(DeskShareNotifyViewersRTMPEventMessage msg) {
|
||||
Map<String, Object> messageInfo = new HashMap<String, Object>();
|
||||
|
||||
// split the string streamPath if there are params in the format:
|
||||
// {channels=2,samplerate=48000,vw=1920,vh=1080,fps=5.00}rtmp://192.168.23.3/video-broadcast/.../..."
|
||||
String fullPathString = msg.streamPath;
|
||||
String delims = "[,{}]+";
|
||||
String[] arr = fullPathString.split(delims);
|
||||
String rtmpStreamPath = arr[arr.length -1];
|
||||
|
||||
messageInfo.put("rtmpUrl", rtmpStreamPath);
|
||||
messageInfo.put("broadcasting", msg.broadcasting);
|
||||
messageInfo.put("width", msg.vw);
|
||||
messageInfo.put("height", msg.vh);
|
||||
|
||||
BroadcastClientMessage m = new BroadcastClientMessage(msg.meetingId, "DeskShareRTMPBroadcastNotification", messageInfo);
|
||||
service.sendMessage(m);
|
||||
}
|
||||
|
||||
private void processDeskShareNotifyASingleViewerEventMessage(DeskShareNotifyASingleViewerEventMessage msg) {
|
||||
Map<String, Object> messageInfo = new HashMap<String, Object>();
|
||||
|
||||
messageInfo.put("rtmpUrl", msg.streamPath);
|
||||
messageInfo.put("broadcasting", msg.broadcasting);
|
||||
messageInfo.put("width", msg.vw);
|
||||
messageInfo.put("height", msg.vh);
|
||||
|
||||
String toUserId = msg.userId;
|
||||
DirectClientMessage receiver = new DirectClientMessage(msg.meetingId, toUserId,
|
||||
"DeskShareRTMPBroadcastNotification", messageInfo);
|
||||
service.sendMessage(receiver);
|
||||
}
|
||||
|
||||
}
|
@ -234,6 +234,10 @@ public class MessagePublisher {
|
||||
sender.send(MessagingConstants.TO_CHAT_CHANNEL, msg.toJson());
|
||||
}
|
||||
|
||||
public void requestDeskShareInfo(String meetingID, String requesterID, String replyTo) {
|
||||
DeskShareGetInfoRequestMessage msg = new DeskShareGetInfoRequestMessage(meetingID, requesterID, replyTo);
|
||||
sender.send(MessagingConstants.FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson());
|
||||
}
|
||||
public void sendWhiteboardAnnotation(String meetingID, String requesterID, Map<String, Object> annotation) {
|
||||
SendWhiteboardAnnotationRequestMessage msg = new SendWhiteboardAnnotationRequestMessage(meetingID, requesterID, annotation);
|
||||
sender.send(MessagingConstants.TO_WHITEBOARD_CHANNEL, msg.toJson());
|
||||
|
@ -7,6 +7,7 @@ import org.bigbluebutton.red5.client.PresentationClientMessageSender;
|
||||
import org.bigbluebutton.red5.client.UserClientMessageSender;
|
||||
import org.bigbluebutton.red5.client.ChatClientMessageSender;
|
||||
import org.bigbluebutton.red5.client.WhiteboardClientMessageSender;
|
||||
import org.bigbluebutton.red5.client.DeskShareMessageSender;
|
||||
import org.bigbluebutton.red5.client.messaging.ConnectionInvokerService;
|
||||
import org.bigbluebutton.red5.monitoring.BbbAppsIsKeepAliveHandler;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
@ -19,8 +20,9 @@ public class RedisPubSubMessageHandler implements MessageHandler {
|
||||
private UserClientMessageSender userMessageSender;
|
||||
private MeetingClientMessageSender meetingMessageSender;
|
||||
private ChatClientMessageSender chatMessageSender;
|
||||
private PresentationClientMessageSender presentationMessageSender;
|
||||
private PresentationClientMessageSender presentationMessageSender;
|
||||
private WhiteboardClientMessageSender whiteboardMessageSender;
|
||||
private DeskShareMessageSender deskShareMessageSender;
|
||||
private BbbAppsIsKeepAliveHandler bbbAppsIsKeepAliveHandler;
|
||||
private PollingClientMessageSender pollingMessageSender;
|
||||
|
||||
@ -31,6 +33,7 @@ public class RedisPubSubMessageHandler implements MessageHandler {
|
||||
chatMessageSender = new ChatClientMessageSender(service);
|
||||
presentationMessageSender = new PresentationClientMessageSender(service);
|
||||
whiteboardMessageSender = new WhiteboardClientMessageSender(service);
|
||||
deskShareMessageSender = new DeskShareMessageSender(service);
|
||||
pollingMessageSender = new PollingClientMessageSender(service);
|
||||
}
|
||||
|
||||
@ -40,6 +43,7 @@ public class RedisPubSubMessageHandler implements MessageHandler {
|
||||
|
||||
@Override
|
||||
public void handleMessage(String pattern, String channel, String message) {
|
||||
// System.out.println("in red5 getting message: " + channel + " " + message);
|
||||
if (channel.equalsIgnoreCase(MessagingConstants.FROM_CHAT_CHANNEL)) {
|
||||
chatMessageSender.handleChatMessage(message);
|
||||
} else if (channel.equalsIgnoreCase(MessagingConstants.FROM_PRESENTATION_CHANNEL)) {
|
||||
@ -53,9 +57,11 @@ public class RedisPubSubMessageHandler implements MessageHandler {
|
||||
whiteboardMessageSender.handleWhiteboardMessage(message);
|
||||
} else if (channel.equalsIgnoreCase(MessagingConstants.FROM_SYSTEM_CHANNEL)) {
|
||||
bbbAppsIsKeepAliveHandler.handleKeepAliveMessage(message);
|
||||
} else if (channel.equalsIgnoreCase(MessagingConstants.FROM_DESK_SHARE_CHANNEL)) {
|
||||
deskShareMessageSender.handleDeskShareMessage(message);
|
||||
} else if (channel.equalsIgnoreCase(MessagingConstants.FROM_POLLING_CHANNEL)) {
|
||||
pollingMessageSender.handlePollMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* 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.red5.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bigbluebutton.red5.BigBlueButtonSession;
|
||||
import org.bigbluebutton.red5.Constants;
|
||||
import org.bigbluebutton.red5.pubsub.MessagePublisher;
|
||||
import org.red5.logging.Red5LoggerFactory;
|
||||
import org.red5.server.api.Red5;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class DesktopSharingService {
|
||||
private static Logger log = Red5LoggerFactory.getLogger( DesktopSharingService.class, "bigbluebutton" );
|
||||
|
||||
private MessagePublisher red5BBBInGw;
|
||||
|
||||
private BigBlueButtonSession getBbbSession() {
|
||||
return (BigBlueButtonSession) Red5.getConnectionLocal().getAttribute(Constants.SESSION);
|
||||
}
|
||||
|
||||
public void setRed5Publisher(MessagePublisher inGW) {
|
||||
red5BBBInGw = inGW;
|
||||
}
|
||||
|
||||
public void requestDeskShareInfo() {
|
||||
String meetingID = Red5.getConnectionLocal().getScope().getName();
|
||||
String requesterID = getBbbSession().getInternalUserID();
|
||||
// Just hardcode as we don't really need it for flash client.
|
||||
String replyTo = meetingID + "/" + requesterID;
|
||||
|
||||
System.out.println("\nDESKTOP SHARING SERVICE _ on the way to bbb-core\n");
|
||||
red5BBBInGw.requestDeskShareInfo(meetingID, requesterID, replyTo);
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
package org.bigbluebutton.red5.service;
|
||||
|
||||
public class WhiteboardKeyUtil {
|
||||
|
||||
public static final String TEXT_TYPE = "text";
|
||||
public static final String PENCIL_TYPE = "pencil";
|
||||
public static final String RECTANGLE_TYPE = "rectangle";
|
||||
public static final String ELLIPSE_TYPE = "ellipse";
|
||||
public static final String TRIANGLE_TYPE = "triangle";
|
||||
public static final String LINE_TYPE = "line";
|
||||
|
||||
public static final String TEXT_CREATED_STATUS = "textCreated";
|
||||
public static final String DRAW_START_STATUS = "DRAW_START";
|
||||
public static final String DRAW_END_STATUS = "DRAW_END";
|
||||
|
||||
}
|
@ -56,26 +56,30 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
<property name="red5Publisher"> <ref bean="red5Publisher"/> </property>
|
||||
</bean>
|
||||
|
||||
<bean id="connInvokerService" class="org.bigbluebutton.red5.client.messaging.ConnectionInvokerService"
|
||||
<bean id="connInvokerService" class="org.bigbluebutton.red5.client.messaging.ConnectionInvokerService"
|
||||
init-method="start" destroy-method="stop"/>
|
||||
|
||||
<bean id="layout.service" class="org.bigbluebutton.red5.service.LayoutService">
|
||||
<property name="red5Publisher"> <ref bean="red5Publisher"/></property>
|
||||
</bean>
|
||||
|
||||
</bean>
|
||||
|
||||
<bean id="desktopSharing.service" class="org.bigbluebutton.red5.service.DesktopSharingService">
|
||||
<property name="red5Publisher"><ref bean="red5Publisher"/></property>
|
||||
</bean>
|
||||
|
||||
<bean id="lock.service" class="org.bigbluebutton.red5.service.LockService">
|
||||
<property name="red5Publisher"><ref bean="red5Publisher"/></property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="chat.service" class="org.bigbluebutton.red5.service.ChatService">
|
||||
<property name="red5Publisher"> <ref bean="red5Publisher"/></property>
|
||||
<property name="maxMessageLength" value="1024"/>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="participants.service" class="org.bigbluebutton.red5.service.ParticipantsService">
|
||||
<property name="red5Publisher"> <ref bean="red5Publisher"/> </property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="presentationApplication" class="org.bigbluebutton.red5.service.PresentationApplication">
|
||||
<property name="red5Publisher"><ref bean="red5Publisher"/></property>
|
||||
</bean>
|
||||
|
@ -11,10 +11,10 @@
|
||||
<property name="themeFile" value="BBBDefault.css"/>
|
||||
<property name="blackTheme" value="BBBBlack.css"/>
|
||||
<property name="LOCALE" value="en_US" />
|
||||
<property name="RESOURCES_DIR" value="${BASE_DIR}/resources" />
|
||||
<property name="PROD_RESOURCES_DIR" value="${RESOURCES_DIR}/prod" />
|
||||
<property name="RESOURCES_DIR" value="${BASE_DIR}/resources" />
|
||||
<property name="PROD_RESOURCES_DIR" value="${RESOURCES_DIR}/prod" />
|
||||
<property name="SRC_DIR" value="${BASE_DIR}/src" />
|
||||
|
||||
|
||||
<property name="OUTPUT_DIR" value="${BASE_DIR}/client" />
|
||||
<taskdef resource="flexTasks.tasks" classpath="${FLEX_HOME}/ant/lib/flexTasks.jar" />
|
||||
|
||||
@ -37,31 +37,31 @@
|
||||
<property name="POLLING" value="PollingModule" />
|
||||
<property name="LAYOUT" value="LayoutModule" />
|
||||
<property name="USERS" value="UsersModule" />
|
||||
|
||||
|
||||
<xmlproperty file="${SRC_DIR}/conf/locales.xml" collapseAttributes="true"/>
|
||||
|
||||
|
||||
<target name="init-ant-contrib">
|
||||
<property name="ant-contrib.jar" location="${BASE_DIR}/build/lib/ant-contrib-0.6.jar"/>
|
||||
<taskdef resource="net/sf/antcontrib/antcontrib.properties" classpath="${ant-contrib.jar}"/>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="locales" depends="init-ant-contrib">
|
||||
<echo message="Checking if locale output dir exists"/>
|
||||
<available file="${OUTPUT_DIR}/locale" type="file" property="locale.dir.present"/>
|
||||
<if>
|
||||
<equals arg1="${locale.dir.present}" arg2="true"/>
|
||||
<then>
|
||||
<echo message="Locale output dir exists. Deleting contents of ${OUTPUT_DIR}/locale"/>
|
||||
<if>
|
||||
<equals arg1="${locale.dir.present}" arg2="true"/>
|
||||
<then>
|
||||
<echo message="Locale output dir exists. Deleting contents of ${OUTPUT_DIR}/locale"/>
|
||||
<delete>
|
||||
<fileset dir="${OUTPUT_DIR}/locale">
|
||||
<include name="**/*"/>
|
||||
</fileset>
|
||||
</delete>
|
||||
</then>
|
||||
<else>
|
||||
<echo message="Locale output dir does not exists. Creating ${OUTPUT_DIR}/locale"/>
|
||||
</then>
|
||||
<else>
|
||||
<echo message="Locale output dir does not exists. Creating ${OUTPUT_DIR}/locale"/>
|
||||
<mkdir dir="${OUTPUT_DIR}/locale"/>
|
||||
</else>
|
||||
</else>
|
||||
</if>
|
||||
|
||||
<echo message="Determining supported locales."/>
|
||||
@ -74,38 +74,38 @@
|
||||
</path>
|
||||
</foreach>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="branding" depends="init-ant-contrib">
|
||||
<sequential>
|
||||
<mxmlc file="${BASE_DIR}/branding/default/style/css/${themeFile}"
|
||||
output="${OUTPUT_DIR}/branding/css/${themeFile}.swf"
|
||||
debug="${DEBUG}"
|
||||
mxml.compatibility-version="3.0.0"
|
||||
swf-version="13"
|
||||
<mxmlc file="${BASE_DIR}/branding/default/style/css/${themeFile}"
|
||||
output="${OUTPUT_DIR}/branding/css/${themeFile}.swf"
|
||||
debug="${DEBUG}"
|
||||
mxml.compatibility-version="3.0.0"
|
||||
swf-version="13"
|
||||
optimize="true">
|
||||
</mxmlc>
|
||||
</sequential>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="branding-black" depends="init-ant-contrib">
|
||||
<sequential>
|
||||
<mxmlc file="${BASE_DIR}/branding/default/style/css/${blackTheme}"
|
||||
output="${OUTPUT_DIR}/branding/css/${blackTheme}.swf"
|
||||
debug="${DEBUG}"
|
||||
mxml.compatibility-version="3.0.0"
|
||||
swf-version="13"
|
||||
<mxmlc file="${BASE_DIR}/branding/default/style/css/${blackTheme}"
|
||||
output="${OUTPUT_DIR}/branding/css/${blackTheme}.swf"
|
||||
debug="${DEBUG}"
|
||||
mxml.compatibility-version="3.0.0"
|
||||
swf-version="13"
|
||||
optimize="true">
|
||||
</mxmlc>
|
||||
</sequential>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-bbb-main-test" description="Compile BigBlueButton Main Test">
|
||||
<build-main src="${SRC_DIR}" target="${BBB_MAIN_TEST}" />
|
||||
</target>
|
||||
|
||||
<target name="build-locale">
|
||||
<echo message="Locale dir is ${supportedlocale}. Extract locale name." />
|
||||
<basename property="locale.name" file="${supportedlocale}"/>
|
||||
<basename property="locale.name" file="${supportedlocale}"/>
|
||||
<echo message="Locale name is ${locale.name}"/>
|
||||
|
||||
<sequential>
|
||||
@ -115,33 +115,33 @@
|
||||
<equals arg1="${locale.name}" arg2="locale"/>
|
||||
<then>
|
||||
<echo message="Somehow, the dirset for locales is passing the parent (${locale.name}) dir."/>
|
||||
<echo message="We don't want it, so we need to skip it."/>
|
||||
<echo message="We don't want it, so we need to skip it."/>
|
||||
</then>
|
||||
<else>
|
||||
<if>
|
||||
<equals arg1="${locale.dir.present}" arg2="true"/>
|
||||
<then>
|
||||
<else>
|
||||
<if>
|
||||
<equals arg1="${locale.dir.present}" arg2="true"/>
|
||||
<then>
|
||||
<echo message="We already have a copy of the framework locale. No need to copy ${LOCALE_DIR}/${locale.name}"/>
|
||||
</then>
|
||||
<else>
|
||||
<echo message="No copy of the framework locale. Copying ${LOCALE_DIR}/${locale.name}"/>
|
||||
</then>
|
||||
<else>
|
||||
<echo message="No copy of the framework locale. Copying ${LOCALE_DIR}/${locale.name}"/>
|
||||
<exec vmlauncher="true" executable="copylocale">
|
||||
<arg value="en_US"/>
|
||||
<arg value="${locale.name}"/>
|
||||
</exec>
|
||||
</exec>
|
||||
</else>
|
||||
</if>
|
||||
</if>
|
||||
<echo message="Compiling locale ${locale.name}"/>
|
||||
<compileLocale locale="${locale.name}" />
|
||||
</else>
|
||||
</if>
|
||||
<compileLocale locale="${locale.name}" />
|
||||
</else>
|
||||
</if>
|
||||
</sequential>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="localize">
|
||||
<compileLocale locale="${LOCALE}" />
|
||||
<compileLocale locale="${LOCALE}" />
|
||||
</target>
|
||||
|
||||
|
||||
<macrodef name="compileLocale" description="Compiles the Resource package for the given locale">
|
||||
<attribute name="locale" default="en_US"/>
|
||||
<sequential>
|
||||
@ -149,13 +149,13 @@
|
||||
<echo message="**********************************************"/>
|
||||
<echo message="* Did you check bundles.txt and made *"/>
|
||||
<echo message="* all resources listed here? *"/>
|
||||
<echo message="**********************************************"/>
|
||||
<echo message="**********************************************"/>
|
||||
<!-- Invoke MXMLC -->
|
||||
<mxmlc output="${OUTPUT_DIR}/locale/@{locale}_resources.swf">
|
||||
<locale>@{locale}</locale>
|
||||
<target-player>11</target-player>
|
||||
<source-path path-element="locale/{locale}"/>
|
||||
|
||||
|
||||
<!--
|
||||
Look into bundles.txt to find out what resources to include here.
|
||||
http://forums.adobe.com/thread/758619
|
||||
@ -163,47 +163,47 @@
|
||||
-->
|
||||
<include-resource-bundles>SharedResources</include-resource-bundles>
|
||||
<include-resource-bundles>bbbResources</include-resource-bundles>
|
||||
<include-resource-bundles>collections</include-resource-bundles>
|
||||
<include-resource-bundles>containers</include-resource-bundles>
|
||||
<include-resource-bundles>controls</include-resource-bundles>
|
||||
<include-resource-bundles>core</include-resource-bundles>
|
||||
<include-resource-bundles>effects</include-resource-bundles>
|
||||
<include-resource-bundles>logging</include-resource-bundles>
|
||||
<include-resource-bundles>messaging</include-resource-bundles>
|
||||
<include-resource-bundles>modules</include-resource-bundles>
|
||||
<include-resource-bundles>rpc</include-resource-bundles>
|
||||
<include-resource-bundles>skins</include-resource-bundles>
|
||||
<include-resource-bundles>collections</include-resource-bundles>
|
||||
<include-resource-bundles>containers</include-resource-bundles>
|
||||
<include-resource-bundles>controls</include-resource-bundles>
|
||||
<include-resource-bundles>core</include-resource-bundles>
|
||||
<include-resource-bundles>effects</include-resource-bundles>
|
||||
<include-resource-bundles>logging</include-resource-bundles>
|
||||
<include-resource-bundles>messaging</include-resource-bundles>
|
||||
<include-resource-bundles>modules</include-resource-bundles>
|
||||
<include-resource-bundles>rpc</include-resource-bundles>
|
||||
<include-resource-bundles>skins</include-resource-bundles>
|
||||
<include-resource-bundles>styles</include-resource-bundles>
|
||||
<source-path path-element="${FLEX_HOME}/frameworks"/>
|
||||
</mxmlc>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
|
||||
<target name="build-bbb-main" description="Compile BigBlueButton Main">
|
||||
<build-main src="${SRC_DIR}" target="${BBB_MAIN}" />
|
||||
|
||||
|
||||
<echo message="Copying common assets for BBB Main" />
|
||||
<copy todir="${OUTPUT_DIR}/org/bigbluebutton/common/assets/images" >
|
||||
<fileset dir="${BASE_DIR}/src/org/bigbluebutton/common/assets/images/" />
|
||||
</copy>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
<target name="build-broadcast" description="Compile Broadcast Module" >
|
||||
<build-module src="${SRC_DIR}" target="${BROADCAST}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-chat" description="Compile Chat Module">
|
||||
<build-module src="${SRC_DIR}" target="${CHAT}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-notes" description="Compile Notes Module">
|
||||
<build-module src="${SRC_DIR}" target="${NOTES}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-polling" description="Compile Polling Module">
|
||||
<build-module src="${SRC_DIR}" target="${POLLING}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-present" description="Compile Present Module">
|
||||
<build-module src="${SRC_DIR}" target="${PRESENT}" />
|
||||
</target>
|
||||
@ -212,12 +212,12 @@
|
||||
<echo message="Compiling deskshare standalone without optimization." />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${DESKSHARE_SA}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-deskshare-no-linker" description="Compile Deskshare Module without the linker">
|
||||
<echo message="Compiling deskshare without optimization." />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${DESKSHARE}" />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${DESKSHARE}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-webcam-preview-standalone" description="Compile Webcam Preview Standalone Application">
|
||||
<echo message="Compiling webcam preview standalone without optimization." />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${CAM_PREVIEW_SA}" />
|
||||
@ -237,28 +237,28 @@
|
||||
<echo message="Compiling Camera Check Application." />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${CAMERA_CHECK}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-conn-check" description="Compile Connection Check Application">
|
||||
<echo message="Compiling Connection Check Application." />
|
||||
<build-module-no-link src="${SRC_DIR}" target="${CONNECTION_CHECK}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-deskshare" description="Compile Deskshare Module">
|
||||
<build-module src="${SRC_DIR}" target="${DESKSHARE}" />
|
||||
<echo message="Copying deskshare applet for Deskshare Module" />
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb-deskshare-applet-unsigned-0.9.0.jar" todir="${OUTPUT_DIR}"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb-deskshare-applet-0.9.0.jar" todir="${OUTPUT_DIR}"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb-deskshare-applet-unsigned-0.9.0.jar" todir="${OUTPUT_DIR}"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb-deskshare-applet-0.9.0.jar" todir="${OUTPUT_DIR}"/>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-phone" description="Compile Phone Module">
|
||||
<build-module src="${SRC_DIR}" target="${PHONE}" />
|
||||
|
||||
|
||||
<echo message="Copying assets for Phone Module" />
|
||||
<copy todir="${OUTPUT_DIR}/org/bigbluebutton/modules/phone/views/assets/images/" >
|
||||
<fileset dir="${BASE_DIR}/src/org/bigbluebutton/modules/phone/views/assets/images/" />
|
||||
</copy>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-video" description="Compile Video Module">
|
||||
<build-module src="${SRC_DIR}" target="${VIDEO}" />
|
||||
</target>
|
||||
@ -266,27 +266,27 @@
|
||||
<target name="build-whiteboard" description="Compile Whiteboard Module">
|
||||
<build-module src="${SRC_DIR}" target="${WHITEBOARD}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-layout" description="Compile Layout Module">
|
||||
<build-module src="${SRC_DIR}" target="${LAYOUT}" />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="build-users" description="Compile Users Module">
|
||||
<build-module src="${SRC_DIR}" target="${USERS}" />
|
||||
</target>
|
||||
|
||||
|
||||
<!-- just a grouping of modules to compile -->
|
||||
<target name="build-main-chat-present"
|
||||
<target name="build-main-chat-present"
|
||||
depends="build-bbb-main, build-chat, build-present, build-layout, build-broadcast, build-users"
|
||||
description="Compile main, chat, present modules">
|
||||
</target>
|
||||
|
||||
|
||||
<!-- just a grouping of modules to compile -->
|
||||
<target name="build-deskshare-phone-video-whiteboard-dyn"
|
||||
<target name="build-deskshare-phone-video-whiteboard-dyn"
|
||||
depends="build-deskshare, build-phone, build-video, build-whiteboard, build-notes, build-polling"
|
||||
description="Compile deskshare, phone, video, whiteboard modules">
|
||||
</target>
|
||||
|
||||
|
||||
<macrodef name="build-main">
|
||||
<attribute name="target" description="Module to compile" />
|
||||
<attribute name="flex" default="${FLEX_HOME}" description="Location of the Flex install." />
|
||||
@ -297,7 +297,7 @@
|
||||
<target-player>11</target-player>
|
||||
<load-config filename="@{flex}/frameworks/flex-config.xml" />
|
||||
<source-path path-element="@{flex}/frameworks" />
|
||||
|
||||
|
||||
<!--
|
||||
Dump out resources to find out what resources to include building the locales.
|
||||
http://forums.adobe.com/thread/758619
|
||||
@ -305,7 +305,7 @@
|
||||
-->
|
||||
<resource-bundle-list>bundles.txt</resource-bundle-list>
|
||||
<static-link-runtime-shared-libraries>${STATIC_RSL}</static-link-runtime-shared-libraries>
|
||||
|
||||
|
||||
<compiler.library-path dir="@{flex}/frameworks" append="true">
|
||||
<include name="libs" />
|
||||
<include name="../bundles/{locale}" />
|
||||
@ -325,7 +325,7 @@
|
||||
<echo message="**********************************************"/>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
|
||||
<macrodef name="build-module-no-link">
|
||||
<attribute name="target" description="Module to compile" />
|
||||
<attribute name="flex" default="${FLEX_HOME}" description="Location of the Flex install." />
|
||||
@ -352,7 +352,7 @@
|
||||
</mxmlc>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
|
||||
<macrodef name="build-module">
|
||||
<attribute name="target" description="Module to compile" />
|
||||
<attribute name="flex" default="${FLEX_HOME}" description="Location of the Flex install." />
|
||||
@ -381,12 +381,12 @@
|
||||
</macrodef>
|
||||
|
||||
<target name="compile-deskshare-standalone" depends="build-deskshare-standalone"
|
||||
description="Compiling standalone desktop sharing">
|
||||
description="Compiling standalone desktop sharing">
|
||||
<echo message="Deskshare standalone built without optimization." />
|
||||
</target>
|
||||
|
||||
|
||||
<target name="compile-bbb" depends="build-main-chat-present, build-deskshare-phone-video-whiteboard-dyn, copy-resource-files"
|
||||
description="Compiling the BBB without copying config.xml">
|
||||
description="Compiling the BBB without copying config.xml">
|
||||
</target>
|
||||
|
||||
<target name="copy-config-if-needed">
|
||||
@ -396,7 +396,7 @@
|
||||
<copy file="/var/www/bigbluebutton/client/conf/config.xml" todir="${BASE_DIR}/src/conf" />
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
</target>
|
||||
|
||||
<target name="copy-join-mock-if-needed">
|
||||
<if>
|
||||
@ -405,23 +405,23 @@
|
||||
<copy file="${RESOURCES_DIR}/dev/join-mock.xml" todir="${BASE_DIR}/src/conf" />
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
</target>
|
||||
|
||||
<target name="copy-resource-files" >
|
||||
<copy todir="${OUTPUT_DIR}/swfobject/" >
|
||||
<fileset dir="${PROD_RESOURCES_DIR}/swfobject" />
|
||||
</copy>
|
||||
</copy>
|
||||
<copy todir="${OUTPUT_DIR}/lib/" >
|
||||
<fileset dir="${PROD_RESOURCES_DIR}/lib"/>
|
||||
</copy>
|
||||
</copy>
|
||||
<copy file="${PROD_RESOURCES_DIR}/BigBlueButtonTest.html" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/BigBlueButton.html" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/DeskshareStandalone.html" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/get_flash_player.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/get_flash_player.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/bbb.gif" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/avatar.png" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/locales.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/expressInstall.swf" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/locales.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/expressInstall.swf" todir="${OUTPUT_DIR}" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/example-info-data.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/layout.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
<copy file="${PROD_RESOURCES_DIR}/profiles.xml" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
@ -429,17 +429,17 @@
|
||||
<equals arg1="${BUILD_ENV}" arg2="DEV"/>
|
||||
<then>
|
||||
<echo message="Copying config.xml for development environment"/>
|
||||
<copy file="${BASE_DIR}/src/conf/config.xml" todir="${OUTPUT_DIR}/conf" />
|
||||
<copy file="${BASE_DIR}/src/conf/config.xml" todir="${OUTPUT_DIR}/conf" />
|
||||
<!-- echo message="Copying layout.xml for development environment"/>
|
||||
<copy file="${BASE_DIR}/src/conf/layout.xml" todir="${OUTPUT_DIR}/conf" /-->
|
||||
<copy file="${BASE_DIR}/src/conf/layout.xml" todir="${OUTPUT_DIR}/conf" /-->
|
||||
</then>
|
||||
<else>
|
||||
<echo message="Need to copy config.xml.template for production environment"/>
|
||||
<copy file="${RESOURCES_DIR}/config.xml.template" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
<copy file="${RESOURCES_DIR}/config.xml.template" todir="${OUTPUT_DIR}/conf" overwrite="true"/>
|
||||
</else>
|
||||
</if>
|
||||
</target>
|
||||
|
||||
|
||||
|
||||
<target name="generate-html-wrapper">
|
||||
<html-wrapper
|
||||
@ -464,9 +464,9 @@
|
||||
<mkdir dir="${BASE_DIR}/asdoc" />
|
||||
<!-- asdoc task not natively supported for ant flexTasks.jar for flex3. It is supported for flex 4, so it should be enabled here
|
||||
when bbb-client is moved to Flex 4 -->
|
||||
<!--<asdoc output="${BASE_DIR}/asdoc"
|
||||
external-library-path="{BASE_DIR}/libs"
|
||||
lenient="true"
|
||||
<!--<asdoc output="${BASE_DIR}/asdoc"
|
||||
external-library-path="{BASE_DIR}/libs"
|
||||
lenient="true"
|
||||
failonerror="true"
|
||||
source-path="${SRC_DIR}"
|
||||
doc-sources="${SRC_DIR}"
|
||||
@ -504,18 +504,16 @@
|
||||
</if>
|
||||
</target>
|
||||
|
||||
<!-- NOTE: compile-deskshare-standalone MUST come first before compile-bbb as we need the deskshare-standalone
|
||||
to be compiled withouth being optimized by using the linker -->
|
||||
<target name="clean-build-bbb" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone,
|
||||
build-webcam-preview-standalone, build-webcam-view-standalone, compile-bbb"
|
||||
description="Build BBB client skipping compiling of locales"/>
|
||||
<target name="clean-build-bbb" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone,
|
||||
build-webcam-preview-standalone, build-webcam-view-standalone, compile-bbb"
|
||||
description="Build BBB client skipping compiling of locales"/>
|
||||
<target name="clean-build-all" depends="clean, init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone, build-mic-check,
|
||||
build-cam-check, build-conn-check, build-webcam-preview-standalone, build-webcam-view-standalone,
|
||||
compile-bbb, branding, branding-black"
|
||||
build-cam-check, build-conn-check, build-webcam-preview-standalone, build-webcam-view-standalone,
|
||||
compile-bbb, branding, branding-black"
|
||||
description="Build BBB client including locales"/>
|
||||
<target name="modules" depends="init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone,
|
||||
<target name="modules" depends="init-ant-contrib, generate-html-wrapper, compile-deskshare-standalone,
|
||||
build-webcam-preview-standalone, build-webcam-view-standalone, compile-bbb"
|
||||
description="Build BBB client without locales"/>
|
||||
description="Build BBB client without locales"/>
|
||||
<target name="cleanandmake" depends="clean-build-all" description="Build BBB client including locales"/>
|
||||
|
||||
<target name="build-poll" depends="init-ant-contrib, build-polling" description="Build only the polling module." />
|
||||
|
@ -41,7 +41,6 @@
|
||||
baseTabIndex="301"
|
||||
/>
|
||||
|
||||
|
||||
<module name="DeskShareModule"
|
||||
url="http://HOST/client/DeskShareModule.swf?v=VERSION"
|
||||
uri="rtmp://HOST/deskShare"
|
||||
@ -51,10 +50,16 @@
|
||||
autoStart="false"
|
||||
autoFullScreen="false"
|
||||
baseTabIndex="201"
|
||||
useWebRTCIfAvailable="false"
|
||||
chromeExtensionKey=""
|
||||
vertoPort="PORT"
|
||||
hostName="HOST.NAME"
|
||||
login="LOGIN"
|
||||
password="PASSWORD"
|
||||
/>
|
||||
|
||||
<module name="PhoneModule" url="http://HOST/client/PhoneModule.swf?v=VERSION"
|
||||
uri="rtmp://HOST/sip"
|
||||
|
||||
<module name="PhoneModule" url="http://HOST/client/PhoneModule.swf?v=VERSION"
|
||||
uri="rtmp://HOST/sip"
|
||||
autoJoin="true"
|
||||
listenOnlyMode="true"
|
||||
presenterShareOnly="false"
|
||||
|
@ -14,10 +14,28 @@
|
||||
swfobject.registerObject("WebcamPreviewStandalone", "11", "expressInstall.swf");
|
||||
swfobject.registerObject("WebcamViewStandalone", "11", "expressInstall.swf");
|
||||
</script>
|
||||
<script src="http://192.168.0.249/client/lib/jquery-1.5.1.min.js" language="javascript"></script>
|
||||
|
||||
<!--<script src="http://192.168.0.249/client/lib/jquery-1.5.1.min.js" language="javascript"></script>-->
|
||||
<script src="http://192.168.0.249/client/lib/bigbluebutton.js" language="javascript"></script>
|
||||
<script src="http://192.168.0.249/client/lib/bbb_localization.js" language="javascript"></script>
|
||||
<script src="http://192.168.0.249/client/lib/bbb_blinker.js" language="javascript"></script>
|
||||
|
||||
<script src="http://192.168.0.249/client/lib/jquery.mobile.min.js" language="javascript"></script>
|
||||
<script src="http://192.168.0.249/client/lib/jquery.json-2.4.min.js" language="javascript"></script>
|
||||
<!-- <script src="http://192.168.0.249/client/lib/getScreenId.js" language="javascript"></script> -->
|
||||
<script src="http://192.168.0.249/client/lib/jquery.cookie.js" language="javascript"></script>
|
||||
<script src="http://192.168.0.249/client/lib/jquery.dataTables.min.js" language="javascript"></script>
|
||||
|
||||
<script src="lib/getScreenId.js" language="javascript"></script>
|
||||
<script src="lib/jquery.FSRTC.js" language="javascript"></script>
|
||||
<script src="lib/jquery.jsonrpcclient.js" language="javascript"></script>
|
||||
<script src="lib/jquery.verto.js" language="javascript"></script>
|
||||
<script src="lib/Screen-Capturing.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension_share.js" language="javascript"></script>
|
||||
|
||||
<!-- <script src="http://192.168.0.249/client/lib/verto_extension.js" language="javascript"></script> -->
|
||||
<!-- <script src="http://192.168.0.249/client/lib/verto_extension_share.js" language="javascript"></script> -->
|
||||
<script src="http://192.168.0.249/client/lib/bbb_deskshare.js" language="javascript"></script>
|
||||
<script type="text/javascript" src="http://192.168.0.249/client/lib/bbb_api_bridge.js"></script>
|
||||
<script type="text/javascript" src="http://192.168.0.249/client/lib/bbb_api_cam_preview.js"></script>
|
||||
|
@ -17,12 +17,12 @@
|
||||
width: 1px !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
#deployJavaPlugin {
|
||||
display : none;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<script type="text/javascript" src="swfobject/swfobject.js"></script>
|
||||
<script src="lib/deployJava.js?v=VERSION" language="javascript"></script>
|
||||
<script type="text/javascript">
|
||||
@ -63,7 +63,7 @@
|
||||
attributes.align = "middle";
|
||||
attributes.tabIndex = 0;
|
||||
swfobject.embedSWF("BigBlueButton.swf?v=VERSION", "altFlash", "100%", "100%", "11.0.0", "expressInstall.swf", flashvars, params, attributes, embedCallback);
|
||||
|
||||
|
||||
function embedCallback(e) {
|
||||
// Work around pixel alignment bug with Chrome 21 on Mac.
|
||||
// See: http://code.google.com/p/bigbluebutton/issues/detail?id=1294
|
||||
@ -86,17 +86,32 @@
|
||||
});
|
||||
}
|
||||
</script>
|
||||
<script src="lib/jquery-1.5.1.min.js?v=VERSION" language="javascript"></script>
|
||||
<!--<script src="lib/jquery-1.5.1.min.js?v=VERSION" language="javascript"></script>-->
|
||||
<script src="lib/jquery-2.1.1.min.js" language="javascript"></script>
|
||||
<script src="lib/bbblogger.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/bigbluebutton.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/bbb_localization.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/bbb_blinker.js?v=VERSION" language="javascript"></script>
|
||||
|
||||
<!--<script src="lib/jquery.mobile.min.js" language="javascript"></script>-->
|
||||
<script src="lib/jquery.json-2.4.min.js" language="javascript"></script>
|
||||
<script src="lib/jquery.cookie.js" language="javascript"></script>
|
||||
<!--<script src="lib/jquery.dataTables.min.js" language="javascript"></script>-->
|
||||
|
||||
<script src="lib/getScreenId.js" language="javascript"></script>
|
||||
<script src="lib/jquery.FSRTC.js" language="javascript"></script>
|
||||
<script src="lib/jquery.jsonrpcclient.js" language="javascript"></script>
|
||||
<script src="lib/jquery.verto.js" language="javascript"></script>
|
||||
<script src="lib/Screen-Capturing.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension_share.js" language="javascript"></script>
|
||||
|
||||
<script src="lib/bbb_deskshare.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/bbb_api_bridge.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/sip.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/sip.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/bbb_webrtc_bridge_sip.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/weburl_regex.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/jsnlog.min.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/weburl_regex.js?v=VERSION" language="javascript"></script>
|
||||
<script src="lib/jsnlog.min.js?v=VERSION" language="javascript"></script>
|
||||
<script>
|
||||
window.chatLinkClicked = function(url) {
|
||||
window.open(url, '_blank');
|
||||
@ -153,6 +168,9 @@
|
||||
<body>
|
||||
<div>
|
||||
<audio id="remote-media" autoplay="autoplay"></audio>
|
||||
<video id="localVertoVideo" autoplay="autoplay" style="display: none;">
|
||||
<p>Your browser doesn't support HTML5 video.</p>
|
||||
</video>
|
||||
</div>
|
||||
<div id="accessibile-progress" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" class="visually-hidden">0 %</div>
|
||||
<button id="enterFlash" type="button" class="visually-hidden" onclick="startFlashFocus();">Set focus to client</button>
|
||||
|
@ -12,10 +12,25 @@
|
||||
<script type="text/javascript">
|
||||
swfobject.registerObject("BigBlueButton", "10.3.0", "expressInstall.swf");
|
||||
</script>
|
||||
<script src="lib/jquery-1.5.1.min.js" language="javascript"></script>
|
||||
<script src="lib/jquery-2.1.1.min.js" language="javascript"></script>
|
||||
<!--<script src="lib/jquery-1.5.1.min.js" language="javascript"></script>-->
|
||||
<script src="lib/bigbluebutton.js" language="javascript"></script>
|
||||
<script src="lib/bbb_localization.js" language="javascript"></script>
|
||||
<script src="lib/bbb_blinker.js" language="javascript"></script>
|
||||
|
||||
<script src="lib/jquery.mobile.min.js" language="javascript"></script>
|
||||
<script src="lib/jquery.json-2.4.min.js" language="javascript"></script>
|
||||
<script src="lib/jquery.cookie.js" language="javascript"></script>
|
||||
<script src="lib/jquery.dataTables.min.js" language="javascript"></script>
|
||||
|
||||
<script src="lib/getScreenId.js" language="javascript"></script>
|
||||
<script src="lib/jquery.FSRTC.js" language="javascript"></script>
|
||||
<script src="lib/jquery.jsonrpcclient.js" language="javascript"></script>
|
||||
<script src="lib/jquery.verto.js" language="javascript"></script>
|
||||
<script src="lib/Screen-Capturing.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension.js" language="javascript"></script>
|
||||
<script src="lib/verto_extension_share.js" language="javascript"></script>
|
||||
|
||||
<script src="lib/bbb_deskshare.js" language="javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
159
bigbluebutton-client/resources/prod/lib/Screen-Capturing.js
Executable file
159
bigbluebutton-client/resources/prod/lib/Screen-Capturing.js
Executable file
@ -0,0 +1,159 @@
|
||||
// Last time updated at January 07, 2016
|
||||
// By Daniel Perrone (perroned)
|
||||
|
||||
// Latest file can be found here: https://cdn.webrtc-experiment.com/Screen-Capturing.js
|
||||
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// Documentation - https://github.com/muaz-khan/Chrome-Extensions/tree/master/Screen-Capturing.js
|
||||
// Demo - https://www.webrtc-experiment.com/Screen-Capturing/
|
||||
|
||||
// ___________________
|
||||
// Screen-Capturing.js
|
||||
|
||||
// Source code: https://github.com/muaz-khan/Chrome-Extensions/tree/master/desktopCapture
|
||||
// Google AppStore installation path: https://chrome.google.com/webstore/detail/screen-capturing/ajhifddimkapgcifgcodmmfdlknahffk
|
||||
|
||||
// This JavaScript file is aimed to explain steps needed to integrate above chrome extension
|
||||
// in your own webpages
|
||||
|
||||
// Usage:
|
||||
// getScreenConstraints(function(screen_constraints) {
|
||||
// navigator.webkitGetUserMedia({ video: screen_constraints }, onSuccess, onFailure );
|
||||
// });
|
||||
|
||||
// First Step: Download the extension, modify "manifest.json" and publish to Google AppStore
|
||||
// https://github.com/muaz-khan/Chrome-Extensions/tree/master/desktopCapture#how-to-publish-yourself
|
||||
|
||||
// Second Step: Listen for postMessage handler
|
||||
// postMessage is used to exchange "sourceId" between chrome extension and you webpage.
|
||||
// though, there are tons other options as well, e.g. XHR-signaling, websockets, etc.
|
||||
window.addEventListener('message', function(event) {
|
||||
if (event.origin != window.location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
onMessageCallback(event.data);
|
||||
});
|
||||
|
||||
// and the function that handles received messages
|
||||
|
||||
function onMessageCallback(data) {
|
||||
// "cancel" button is clicked
|
||||
if (data == 'PermissionDeniedError') {
|
||||
chromeMediaSource = 'PermissionDeniedError';
|
||||
if (screenCallback) return screenCallback('PermissionDeniedError');
|
||||
else throw new Error('PermissionDeniedError');
|
||||
}
|
||||
|
||||
// extension notified his presence
|
||||
if (data == 'rtcmulticonnection-extension-loaded') {
|
||||
chromeMediaSource = 'desktop';
|
||||
}
|
||||
|
||||
// extension shared temp sourceId
|
||||
if (data.sourceId && screenCallback) {
|
||||
screenCallback(sourceId = data.sourceId);
|
||||
}
|
||||
}
|
||||
|
||||
// global variables
|
||||
var chromeMediaSource = 'screen';
|
||||
var sourceId;
|
||||
var screenCallback;
|
||||
|
||||
// this method can be used to check if chrome extension is installed & enabled.
|
||||
function isChromeExtensionAvailable(callback) {
|
||||
if (!callback) return;
|
||||
|
||||
if (chromeMediaSource == 'desktop') return callback(true);
|
||||
|
||||
// ask extension if it is available
|
||||
window.postMessage('are-you-there', '*');
|
||||
|
||||
setTimeout(function() {
|
||||
if (chromeMediaSource == 'screen') {
|
||||
callback(false);
|
||||
} else callback(true);
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
// this function can be used to get "source-id" from the extension
|
||||
function getSourceId(callback) {
|
||||
if (!callback) throw '"callback" parameter is mandatory.';
|
||||
if(sourceId) return callback(sourceId);
|
||||
|
||||
screenCallback = callback;
|
||||
window.postMessage('get-sourceId', '*');
|
||||
}
|
||||
|
||||
var isFirefox = typeof window.InstallTrigger !== 'undefined';
|
||||
var isOpera = !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
|
||||
var isChrome = !!window.chrome && !isOpera;
|
||||
|
||||
function getChromeExtensionStatus(extensionid, callback) {
|
||||
callback = normalizeCallback(callback);
|
||||
|
||||
if (isFirefox) return callback('not-chrome');
|
||||
|
||||
if (chromeMediaSource == 'desktop') return callback('installed-enabled');
|
||||
|
||||
if (arguments.length != 2) {
|
||||
callback = extensionid;
|
||||
extensionid = 'ajhifddimkapgcifgcodmmfdlknahffk'; // Muaz Khan's Screen Capturing
|
||||
}
|
||||
|
||||
var image = document.createElement('img');
|
||||
image.src = 'chrome-extension://' + extensionid + '/icon.png';
|
||||
image.onload = function() {
|
||||
chromeMediaSource = 'screen';
|
||||
window.postMessage('are-you-there', '*');
|
||||
setTimeout(function() {
|
||||
callback('installed-enabled');
|
||||
}, 2000);
|
||||
};
|
||||
image.onerror = function() {
|
||||
callback('not-installed');
|
||||
};
|
||||
}
|
||||
|
||||
// this function explains how to use above methods/objects
|
||||
function getScreenConstraints(callback) {
|
||||
var firefoxScreenConstraints = {
|
||||
mozMediaSource: 'window',
|
||||
mediaSource: 'window'
|
||||
};
|
||||
|
||||
if(isFirefox) return callback(null, firefoxScreenConstraints);
|
||||
|
||||
// this statement defines getUserMedia constraints
|
||||
// that will be used to capture content of screen
|
||||
var screen_constraints = {
|
||||
mandatory: {
|
||||
chromeMediaSource: chromeMediaSource,
|
||||
maxWidth: screen.width > 1920 ? screen.width : 1920,
|
||||
maxHeight: screen.height > 1080 ? screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
};
|
||||
|
||||
// this statement verifies chrome extension availability
|
||||
// if installed and available then it will invoke extension API
|
||||
// otherwise it will fallback to command-line based screen capturing API
|
||||
sourceId = null;
|
||||
if (chromeMediaSource == 'desktop') {
|
||||
getSourceId(function() {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
callback(sourceId == 'PermissionDeniedError' ? sourceId : null, screen_constraints);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// this statement sets gets 'sourceId" and sets "chromeMediaSourceId"
|
||||
if (chromeMediaSource == 'desktop') {
|
||||
screen_constraints.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
|
||||
// now invoking native getUserMedia API
|
||||
callback(null, screen_constraints);
|
||||
}
|
103
bigbluebutton-client/resources/prod/lib/getScreenId.js
Executable file
103
bigbluebutton-client/resources/prod/lib/getScreenId.js
Executable file
@ -0,0 +1,103 @@
|
||||
// Last time updated at Sep 07, 2014, 08:32:23
|
||||
|
||||
// Latest file can be found here: https://cdn.webrtc-experiment.com/getScreenId.js
|
||||
|
||||
// Muaz Khan - www.MuazKhan.com
|
||||
// MIT License - www.WebRTC-Experiment.com/licence
|
||||
// Documentation - https://github.com/muaz-khan/WebRTC-Experiment/tree/master/getScreenId.js
|
||||
|
||||
// ______________
|
||||
// getScreenId.js
|
||||
|
||||
/*
|
||||
getScreenId(function (error, sourceId, screen_constraints) {
|
||||
// error == null || 'permission-denied' || 'not-installed' || 'installed-disabled' || 'not-chrome'
|
||||
// sourceId == null || 'string' || 'firefox'
|
||||
|
||||
if(sourceId == 'firefox') {
|
||||
navigator.mozGetUserMedia(screen_constraints, onSuccess, onFailure);
|
||||
}
|
||||
else navigator.webkitGetUserMedia(screen_constraints, onSuccess, onFailure);
|
||||
});
|
||||
*/
|
||||
|
||||
(function() {
|
||||
window.getScreenId = function(callback) {
|
||||
// for Firefox:
|
||||
// sourceId == 'firefox'
|
||||
// screen_constraints = {...}
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
callback(null, 'firefox', {
|
||||
video: {
|
||||
mozMediaSource: 'window',
|
||||
mediaSource: 'window'
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
postMessage();
|
||||
|
||||
window.addEventListener('message', onIFrameCallback);
|
||||
|
||||
function onIFrameCallback(event) {
|
||||
if (!event.data) return;
|
||||
|
||||
if (event.data.chromeMediaSourceId) {
|
||||
if (event.data.chromeMediaSourceId === 'PermissionDeniedError') {
|
||||
callback('permission-denied');
|
||||
} else callback(null, event.data.chromeMediaSourceId, getScreenConstraints(null, event.data.chromeMediaSourceId));
|
||||
}
|
||||
|
||||
if (event.data.chromeExtensionStatus) {
|
||||
callback(event.data.chromeExtensionStatus, null, getScreenConstraints(event.data.chromeExtensionStatus));
|
||||
}
|
||||
|
||||
// this event listener is no more needed
|
||||
window.removeEventListener('message', onIFrameCallback);
|
||||
}
|
||||
};
|
||||
|
||||
function getScreenConstraints(error, sourceId) {
|
||||
var screen_constraints = {
|
||||
audio: false,
|
||||
video: {
|
||||
mandatory: {
|
||||
chromeMediaSource: error ? 'screen' : 'desktop',
|
||||
maxWidth: window.screen.width > 1920 ? window.screen.width : 1920,
|
||||
maxHeight: window.screen.height > 1080 ? window.screen.height : 1080
|
||||
},
|
||||
optional: []
|
||||
}
|
||||
};
|
||||
console.log("____in getScreenConstraints and error=" + error + " and sourceId=" + sourceId +
|
||||
" and chromeMediaSource = " + screen_constraints.video.mandatory.chromeMediaSource);
|
||||
|
||||
if (sourceId) {
|
||||
screen_constraints.video.mandatory.chromeMediaSourceId = sourceId;
|
||||
}
|
||||
|
||||
return screen_constraints;
|
||||
}
|
||||
|
||||
function postMessage() {
|
||||
console.log("___in postMessage and iframe isLoaded=" + iframe.isLoaded);
|
||||
|
||||
if (!iframe.isLoaded) {
|
||||
setTimeout(postMessage, 100);
|
||||
return;
|
||||
}
|
||||
|
||||
iframe.contentWindow.postMessage({
|
||||
captureSourceId: true
|
||||
}, '*');
|
||||
}
|
||||
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.onload = function() {
|
||||
iframe.isLoaded = true;
|
||||
};
|
||||
iframe.src = 'https://www.webrtc-experiment.com/getSourceId/';
|
||||
iframe.style.display = 'none';
|
||||
(document.body || document.documentElement).appendChild(iframe);
|
||||
})();
|
4
bigbluebutton-client/resources/prod/lib/jquery-2.1.1.min.js
vendored
Normal file
4
bigbluebutton-client/resources/prod/lib/jquery-2.1.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1267
bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js
Executable file
1267
bigbluebutton-client/resources/prod/lib/jquery.FSRTC.js
Executable file
File diff suppressed because it is too large
Load Diff
90
bigbluebutton-client/resources/prod/lib/jquery.cookie.js
Normal file
90
bigbluebutton-client/resources/prod/lib/jquery.cookie.js
Normal file
@ -0,0 +1,90 @@
|
||||
/*!
|
||||
* jQuery Cookie Plugin v1.3.1
|
||||
* https://github.com/carhartl/jquery-cookie
|
||||
*
|
||||
* Copyright 2013 Klaus Hartl
|
||||
* Released under the MIT license
|
||||
*/
|
||||
(function ($, document, undefined) {
|
||||
|
||||
var pluses = /\+/g;
|
||||
|
||||
function raw(s) {
|
||||
return s;
|
||||
}
|
||||
|
||||
function decoded(s) {
|
||||
return unRfc2068(decodeURIComponent(s.replace(pluses, ' ')));
|
||||
}
|
||||
|
||||
function unRfc2068(value) {
|
||||
if (value.indexOf('"') === 0) {
|
||||
// This is a quoted cookie as according to RFC2068, unescape
|
||||
value = value.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function fromJSON(value) {
|
||||
return config.json ? JSON.parse(value) : value;
|
||||
}
|
||||
|
||||
var config = $.cookie = function (key, value, options) {
|
||||
|
||||
// write
|
||||
if (value !== undefined) {
|
||||
options = $.extend({}, config.defaults, options);
|
||||
|
||||
if (value === null) {
|
||||
options.expires = -1;
|
||||
}
|
||||
|
||||
if (typeof options.expires === 'number') {
|
||||
var days = options.expires, t = options.expires = new Date();
|
||||
t.setDate(t.getDate() + days);
|
||||
}
|
||||
|
||||
value = config.json ? JSON.stringify(value) : String(value);
|
||||
|
||||
return (document.cookie = [
|
||||
encodeURIComponent(key), '=', config.raw ? value : encodeURIComponent(value),
|
||||
options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE
|
||||
options.path ? '; path=' + options.path : '',
|
||||
options.domain ? '; domain=' + options.domain : '',
|
||||
options.secure ? '; secure' : ''
|
||||
].join(''));
|
||||
}
|
||||
|
||||
// read
|
||||
var decode = config.raw ? raw : decoded;
|
||||
var cookies = document.cookie.split('; ');
|
||||
var result = key ? null : {};
|
||||
for (var i = 0, l = cookies.length; i < l; i++) {
|
||||
var parts = cookies[i].split('=');
|
||||
var name = decode(parts.shift());
|
||||
var cookie = decode(parts.join('='));
|
||||
|
||||
if (key && key === name) {
|
||||
result = fromJSON(cookie);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!key) {
|
||||
result[name] = fromJSON(cookie);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
config.defaults = {};
|
||||
|
||||
$.removeCookie = function (key, options) {
|
||||
if ($.cookie(key) !== null) {
|
||||
$.cookie(key, null, options);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
})(jQuery, document);
|
155
bigbluebutton-client/resources/prod/lib/jquery.dataTables.min.js
vendored
Normal file
155
bigbluebutton-client/resources/prod/lib/jquery.dataTables.min.js
vendored
Normal file
@ -0,0 +1,155 @@
|
||||
/*! DataTables 1.10.1
|
||||
* ©2008-2014 SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
(function(za,O,l){var N=function(h){function T(a){var b,c,d={};h.each(a,function(e){if((b=e.match(/^([^A-Z]+?)([A-Z])/))&&-1!=="a aa ai ao as b fn i m o s ".indexOf(b[1]+" "))c=e.replace(b[0],b[2].toLowerCase()),d[c]=e,"o"===b[1]&&T(a[e])});a._hungarianMap=d}function G(a,b,c){a._hungarianMap||T(a);var d;h.each(b,function(e){d=a._hungarianMap[e];if(d!==l&&(c||b[d]===l))"o"===d.charAt(0)?(b[d]||(b[d]={}),h.extend(!0,b[d],b[e]),G(a[d],b[d],c)):b[d]=b[e]})}function N(a){var b=p.defaults.oLanguage,c=a.sZeroRecords;
|
||||
!a.sEmptyTable&&(c&&"No data available in table"===b.sEmptyTable)&&D(a,a,"sZeroRecords","sEmptyTable");!a.sLoadingRecords&&(c&&"Loading..."===b.sLoadingRecords)&&D(a,a,"sZeroRecords","sLoadingRecords");a.sInfoThousands&&(a.sThousands=a.sInfoThousands);(a=a.sDecimal)&&cb(a)}function db(a){w(a,"ordering","bSort");w(a,"orderMulti","bSortMulti");w(a,"orderClasses","bSortClasses");w(a,"orderCellsTop","bSortCellsTop");w(a,"order","aaSorting");w(a,"orderFixed","aaSortingFixed");w(a,"paging","bPaginate");
|
||||
w(a,"pagingType","sPaginationType");w(a,"pageLength","iDisplayLength");w(a,"searching","bFilter");if(a=a.aoSearchCols)for(var b=0,c=a.length;b<c;b++)a[b]&&G(p.models.oSearch,a[b])}function eb(a){w(a,"orderable","bSortable");w(a,"orderData","aDataSort");w(a,"orderSequence","asSorting");w(a,"orderDataType","sortDataType")}function fb(a){var a=a.oBrowser,b=h("<div/>").css({position:"absolute",top:0,left:0,height:1,width:1,overflow:"hidden"}).append(h("<div/>").css({position:"absolute",top:1,left:1,width:100,
|
||||
overflow:"scroll"}).append(h('<div class="test"/>').css({width:"100%",height:10}))).appendTo("body"),c=b.find(".test");a.bScrollOversize=100===c[0].offsetWidth;a.bScrollbarLeft=1!==c.offset().left;b.remove()}function gb(a,b,c,d,e,f){var g,j=!1;c!==l&&(g=c,j=!0);for(;d!==e;)a.hasOwnProperty(d)&&(g=j?b(g,a[d],d,a):a[d],j=!0,d+=f);return g}function Aa(a,b){var c=p.defaults.column,d=a.aoColumns.length,c=h.extend({},p.models.oColumn,c,{nTh:b?b:O.createElement("th"),sTitle:c.sTitle?c.sTitle:b?b.innerHTML:
|
||||
"",aDataSort:c.aDataSort?c.aDataSort:[d],mData:c.mData?c.mData:d,idx:d});a.aoColumns.push(c);c=a.aoPreSearchCols;c[d]=h.extend({},p.models.oSearch,c[d]);fa(a,d,null)}function fa(a,b,c){var b=a.aoColumns[b],d=a.oClasses,e=h(b.nTh);if(!b.sWidthOrig){b.sWidthOrig=e.attr("width")||null;var f=(e.attr("style")||"").match(/width:\s*(\d+[pxem%]+)/);f&&(b.sWidthOrig=f[1])}c!==l&&null!==c&&(eb(c),G(p.defaults.column,c),c.mDataProp!==l&&!c.mData&&(c.mData=c.mDataProp),c.sType&&(b._sManualType=c.sType),c.className&&
|
||||
!c.sClass&&(c.sClass=c.className),h.extend(b,c),D(b,c,"sWidth","sWidthOrig"),"number"===typeof c.iDataSort&&(b.aDataSort=[c.iDataSort]),D(b,c,"aDataSort"));var g=b.mData,j=U(g),i=b.mRender?U(b.mRender):null,c=function(a){return"string"===typeof a&&-1!==a.indexOf("@")};b._bAttrSrc=h.isPlainObject(g)&&(c(g.sort)||c(g.type)||c(g.filter));b.fnGetData=function(a,b,c){var d=j(a,b,l,c);return i&&b?i(d,b,a,c):d};b.fnSetData=function(a,b,c){return Ba(g)(a,b,c)};a.oFeatures.bSort||(b.bSortable=!1,e.addClass(d.sSortableNone));
|
||||
a=-1!==h.inArray("asc",b.asSorting);c=-1!==h.inArray("desc",b.asSorting);!b.bSortable||!a&&!c?(b.sSortingClass=d.sSortableNone,b.sSortingClassJUI=""):a&&!c?(b.sSortingClass=d.sSortableAsc,b.sSortingClassJUI=d.sSortJUIAscAllowed):!a&&c?(b.sSortingClass=d.sSortableDesc,b.sSortingClassJUI=d.sSortJUIDescAllowed):(b.sSortingClass=d.sSortable,b.sSortingClassJUI=d.sSortJUI)}function V(a){if(!1!==a.oFeatures.bAutoWidth){var b=a.aoColumns;Ca(a);for(var c=0,d=b.length;c<d;c++)b[c].nTh.style.width=b[c].sWidth}b=
|
||||
a.oScroll;(""!==b.sY||""!==b.sX)&&W(a);u(a,null,"column-sizing",[a])}function ga(a,b){var c=X(a,"bVisible");return"number"===typeof c[b]?c[b]:null}function Y(a,b){var c=X(a,"bVisible"),c=h.inArray(b,c);return-1!==c?c:null}function Z(a){return X(a,"bVisible").length}function X(a,b){var c=[];h.map(a.aoColumns,function(a,e){a[b]&&c.push(e)});return c}function Da(a){var b=a.aoColumns,c=a.aoData,d=p.ext.type.detect,e,f,g,j,i,h,m,n,k;e=0;for(f=b.length;e<f;e++)if(m=b[e],k=[],!m.sType&&m._sManualType)m.sType=
|
||||
m._sManualType;else if(!m.sType){g=0;for(j=d.length;g<j;g++){i=0;for(h=c.length;i<h&&!(k[i]===l&&(k[i]=A(a,i,e,"type")),n=d[g](k[i],a),!n||"html"===n);i++);if(n){m.sType=n;break}}m.sType||(m.sType="string")}}function hb(a,b,c,d){var e,f,g,j,i,o,m=a.aoColumns;if(b)for(e=b.length-1;0<=e;e--){o=b[e];var n=o.targets!==l?o.targets:o.aTargets;h.isArray(n)||(n=[n]);f=0;for(g=n.length;f<g;f++)if("number"===typeof n[f]&&0<=n[f]){for(;m.length<=n[f];)Aa(a);d(n[f],o)}else if("number"===typeof n[f]&&0>n[f])d(m.length+
|
||||
n[f],o);else if("string"===typeof n[f]){j=0;for(i=m.length;j<i;j++)("_all"==n[f]||h(m[j].nTh).hasClass(n[f]))&&d(j,o)}}if(c){e=0;for(a=c.length;e<a;e++)d(e,c[e])}}function I(a,b,c,d){var e=a.aoData.length,f=h.extend(!0,{},p.models.oRow,{src:c?"dom":"data"});f._aData=b;a.aoData.push(f);for(var b=a.aoColumns,f=0,g=b.length;f<g;f++)c&&Ea(a,e,f,A(a,e,f)),b[f].sType=null;a.aiDisplayMaster.push(e);(c||!a.oFeatures.bDeferRender)&&Fa(a,e,c,d);return e}function ha(a,b){var c;b instanceof h||(b=h(b));return b.map(function(b,
|
||||
e){c=ia(a,e);return I(a,c.data,e,c.cells)})}function A(a,b,c,d){var e=a.iDraw,f=a.aoColumns[c],g=a.aoData[b]._aData,j=f.sDefaultContent,c=f.fnGetData(g,d,{settings:a,row:b,col:c});if(c===l)return a.iDrawError!=e&&null===j&&(P(a,0,"Requested unknown parameter "+("function"==typeof f.mData?"{function}":"'"+f.mData+"'")+" for row "+b,4),a.iDrawError=e),j;if((c===g||null===c)&&null!==j)c=j;else if("function"===typeof c)return c.call(g);return null===c&&"display"==d?"":c}function Ea(a,b,c,d){a.aoColumns[c].fnSetData(a.aoData[b]._aData,
|
||||
d,{settings:a,row:b,col:c})}function Ga(a){return h.map(a.match(/(\\.|[^\.])+/g),function(a){return a.replace(/\\./g,".")})}function U(a){if(h.isPlainObject(a)){var b={};h.each(a,function(a,c){c&&(b[a]=U(c))});return function(a,c,f,g){var j=b[c]||b._;return j!==l?j(a,c,f,g):a}}if(null===a)return function(a){return a};if("function"===typeof a)return function(b,c,f,g){return a(b,c,f,g)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var c=function(a,b,f){var g,
|
||||
j;if(""!==f){j=Ga(f);for(var i=0,h=j.length;i<h;i++){f=j[i].match($);g=j[i].match(Q);if(f){j[i]=j[i].replace($,"");""!==j[i]&&(a=a[j[i]]);g=[];j.splice(0,i+1);j=j.join(".");i=0;for(h=a.length;i<h;i++)g.push(c(a[i],b,j));a=f[0].substring(1,f[0].length-1);a=""===a?g:g.join(a);break}else if(g){j[i]=j[i].replace(Q,"");a=a[j[i]]();continue}if(null===a||a[j[i]]===l)return l;a=a[j[i]]}}return a};return function(b,e){return c(b,e,a)}}return function(b){return b[a]}}function Ba(a){if(h.isPlainObject(a))return Ba(a._);
|
||||
if(null===a)return function(){};if("function"===typeof a)return function(b,d,e){a(b,"set",d,e)};if("string"===typeof a&&(-1!==a.indexOf(".")||-1!==a.indexOf("[")||-1!==a.indexOf("("))){var b=function(a,d,e){var e=Ga(e),f;f=e[e.length-1];for(var g,j,i=0,h=e.length-1;i<h;i++){g=e[i].match($);j=e[i].match(Q);if(g){e[i]=e[i].replace($,"");a[e[i]]=[];f=e.slice();f.splice(0,i+1);g=f.join(".");j=0;for(h=d.length;j<h;j++)f={},b(f,d[j],g),a[e[i]].push(f);return}j&&(e[i]=e[i].replace(Q,""),a=a[e[i]](d));if(null===
|
||||
a[e[i]]||a[e[i]]===l)a[e[i]]={};a=a[e[i]]}if(f.match(Q))a[f.replace(Q,"")](d);else a[f.replace($,"")]=d};return function(c,d){return b(c,d,a)}}return function(b,d){b[a]=d}}function Ha(a){return C(a.aoData,"_aData")}function ja(a){a.aoData.length=0;a.aiDisplayMaster.length=0;a.aiDisplay.length=0}function ka(a,b,c){for(var d=-1,e=0,f=a.length;e<f;e++)a[e]==b?d=e:a[e]>b&&a[e]--; -1!=d&&c===l&&a.splice(d,1)}function la(a,b,c,d){var e=a.aoData[b],f;if("dom"===c||(!c||"auto"===c)&&"dom"===e.src)e._aData=
|
||||
ia(a,e).data;else{var g=e.anCells,j;if(g){c=0;for(f=g.length;c<f;c++){for(j=g[c];j.childNodes.length;)j.removeChild(j.firstChild);g[c].innerHTML=A(a,b,c,"display")}}}e._aSortData=null;e._aFilterData=null;a=a.aoColumns;if(d!==l)a[d].sType=null;else{c=0;for(f=a.length;c<f;c++)a[c].sType=null}Ia(e)}function ia(a,b){var c=[],d=[],e=b.firstChild,f,g,j,i=0,o,m=a.aoColumns,n=function(a,b,c){"string"===typeof a&&(b=a.indexOf("@"),-1!==b&&(a=a.substring(b+1),j["@"+a]=c.getAttribute(a)))},k=function(a){g=m[i];
|
||||
o=h.trim(a.innerHTML);g&&g._bAttrSrc?(j={display:o},n(g.mData.sort,j,a),n(g.mData.type,j,a),n(g.mData.filter,j,a),c.push(j)):c.push(o);i++};if(e)for(;e;){f=e.nodeName.toUpperCase();if("TD"==f||"TH"==f)k(e),d.push(e);e=e.nextSibling}else{d=b.anCells;e=0;for(f=d.length;e<f;e++)k(d[e])}return{data:c,cells:d}}function Fa(a,b,c,d){var e=a.aoData[b],f=e._aData,g=[],j,i,h,m,n;if(null===e.nTr){j=c||O.createElement("tr");e.nTr=j;e.anCells=g;j._DT_RowIndex=b;Ia(e);m=0;for(n=a.aoColumns.length;m<n;m++){h=a.aoColumns[m];
|
||||
i=c?d[m]:O.createElement(h.sCellType);g.push(i);if(!c||h.mRender||h.mData!==m)i.innerHTML=A(a,b,m,"display");h.sClass&&(i.className+=" "+h.sClass);h.bVisible&&!c?j.appendChild(i):!h.bVisible&&c&&i.parentNode.removeChild(i);h.fnCreatedCell&&h.fnCreatedCell.call(a.oInstance,i,A(a,b,m),f,b,m)}u(a,"aoRowCreatedCallback",null,[j,f,b])}e.nTr.setAttribute("role","row")}function Ia(a){var b=a.nTr,c=a._aData;if(b){c.DT_RowId&&(b.id=c.DT_RowId);if(c.DT_RowClass){var d=c.DT_RowClass.split(" ");a.__rowc=a.__rowc?
|
||||
Ja(a.__rowc.concat(d)):d;h(b).removeClass(a.__rowc.join(" ")).addClass(c.DT_RowClass)}c.DT_RowData&&h(b).data(c.DT_RowData)}}function ib(a){var b,c,d,e,f,g=a.nTHead,j=a.nTFoot,i=0===h("th, td",g).length,o=a.oClasses,m=a.aoColumns;i&&(e=h("<tr/>").appendTo(g));b=0;for(c=m.length;b<c;b++)f=m[b],d=h(f.nTh).addClass(f.sClass),i&&d.appendTo(e),a.oFeatures.bSort&&(d.addClass(f.sSortingClass),!1!==f.bSortable&&(d.attr("tabindex",a.iTabIndex).attr("aria-controls",a.sTableId),Ka(a,f.nTh,b))),f.sTitle!=d.html()&&
|
||||
d.html(f.sTitle),La(a,"header")(a,d,f,o);i&&aa(a.aoHeader,g);h(g).find(">tr").attr("role","row");h(g).find(">tr>th, >tr>td").addClass(o.sHeaderTH);h(j).find(">tr>th, >tr>td").addClass(o.sFooterTH);if(null!==j){a=a.aoFooter[0];b=0;for(c=a.length;b<c;b++)f=m[b],f.nTf=a[b].cell,f.sClass&&h(f.nTf).addClass(f.sClass)}}function ba(a,b,c){var d,e,f,g=[],j=[],i=a.aoColumns.length,o;if(b){c===l&&(c=!1);d=0;for(e=b.length;d<e;d++){g[d]=b[d].slice();g[d].nTr=b[d].nTr;for(f=i-1;0<=f;f--)!a.aoColumns[f].bVisible&&
|
||||
!c&&g[d].splice(f,1);j.push([])}d=0;for(e=g.length;d<e;d++){if(a=g[d].nTr)for(;f=a.firstChild;)a.removeChild(f);f=0;for(b=g[d].length;f<b;f++)if(o=i=1,j[d][f]===l){a.appendChild(g[d][f].cell);for(j[d][f]=1;g[d+i]!==l&&g[d][f].cell==g[d+i][f].cell;)j[d+i][f]=1,i++;for(;g[d][f+o]!==l&&g[d][f].cell==g[d][f+o].cell;){for(c=0;c<i;c++)j[d+c][f+o]=1;o++}h(g[d][f].cell).attr("rowspan",i).attr("colspan",o)}}}}function K(a){var b=u(a,"aoPreDrawCallback","preDraw",[a]);if(-1!==h.inArray(!1,b))B(a,!1);else{var b=
|
||||
[],c=0,d=a.asStripeClasses,e=d.length,f=a.oLanguage,g=a.iInitDisplayStart,j="ssp"==z(a),i=a.aiDisplay;a.bDrawing=!0;g!==l&&-1!==g&&(a._iDisplayStart=j?g:g>=a.fnRecordsDisplay()?0:g,a.iInitDisplayStart=-1);var g=a._iDisplayStart,o=a.fnDisplayEnd();if(a.bDeferLoading)a.bDeferLoading=!1,a.iDraw++,B(a,!1);else if(j){if(!a.bDestroying&&!jb(a))return}else a.iDraw++;if(0!==i.length){f=j?a.aoData.length:o;for(j=j?0:g;j<f;j++){var m=i[j],n=a.aoData[m];null===n.nTr&&Fa(a,m);m=n.nTr;if(0!==e){var k=d[c%e];n._sRowStripe!=
|
||||
k&&(h(m).removeClass(n._sRowStripe).addClass(k),n._sRowStripe=k)}u(a,"aoRowCallback",null,[m,n._aData,c,j]);b.push(m);c++}}else c=f.sZeroRecords,1==a.iDraw&&"ajax"==z(a)?c=f.sLoadingRecords:f.sEmptyTable&&0===a.fnRecordsTotal()&&(c=f.sEmptyTable),b[0]=h("<tr/>",{"class":e?d[0]:""}).append(h("<td />",{valign:"top",colSpan:Z(a),"class":a.oClasses.sRowEmpty}).html(c))[0];u(a,"aoHeaderCallback","header",[h(a.nTHead).children("tr")[0],Ha(a),g,o,i]);u(a,"aoFooterCallback","footer",[h(a.nTFoot).children("tr")[0],
|
||||
Ha(a),g,o,i]);d=h(a.nTBody);d.children().detach();d.append(h(b));u(a,"aoDrawCallback","draw",[a]);a.bSorted=!1;a.bFiltered=!1;a.bDrawing=!1}}function L(a,b){var c=a.oFeatures,d=c.bFilter;c.bSort&&kb(a);d?ca(a,a.oPreviousSearch):a.aiDisplay=a.aiDisplayMaster.slice();!0!==b&&(a._iDisplayStart=0);a._drawHold=b;K(a);a._drawHold=!1}function lb(a){var b=a.oClasses,c=h(a.nTable),c=h("<div/>").insertBefore(c),d=a.oFeatures,e=h("<div/>",{id:a.sTableId+"_wrapper","class":b.sWrapper+(a.nTFoot?"":" "+b.sNoFooter)});
|
||||
a.nHolding=c[0];a.nTableWrapper=e[0];a.nTableReinsertBefore=a.nTable.nextSibling;for(var f=a.sDom.split(""),g,j,i,o,m,n,k=0;k<f.length;k++){g=null;j=f[k];if("<"==j){i=h("<div/>")[0];o=f[k+1];if("'"==o||'"'==o){m="";for(n=2;f[k+n]!=o;)m+=f[k+n],n++;"H"==m?m=b.sJUIHeader:"F"==m&&(m=b.sJUIFooter);-1!=m.indexOf(".")?(o=m.split("."),i.id=o[0].substr(1,o[0].length-1),i.className=o[1]):"#"==m.charAt(0)?i.id=m.substr(1,m.length-1):i.className=m;k+=n}e.append(i);e=h(i)}else if(">"==j)e=e.parent();else if("l"==
|
||||
j&&d.bPaginate&&d.bLengthChange)g=mb(a);else if("f"==j&&d.bFilter)g=nb(a);else if("r"==j&&d.bProcessing)g=ob(a);else if("t"==j)g=pb(a);else if("i"==j&&d.bInfo)g=qb(a);else if("p"==j&&d.bPaginate)g=rb(a);else if(0!==p.ext.feature.length){i=p.ext.feature;n=0;for(o=i.length;n<o;n++)if(j==i[n].cFeature){g=i[n].fnInit(a);break}}g&&(i=a.aanFeatures,i[j]||(i[j]=[]),i[j].push(g),e.append(g))}c.replaceWith(e)}function aa(a,b){var c=h(b).children("tr"),d,e,f,g,j,i,o,m,n,k;a.splice(0,a.length);f=0;for(i=c.length;f<
|
||||
i;f++)a.push([]);f=0;for(i=c.length;f<i;f++){d=c[f];for(e=d.firstChild;e;){if("TD"==e.nodeName.toUpperCase()||"TH"==e.nodeName.toUpperCase()){m=1*e.getAttribute("colspan");n=1*e.getAttribute("rowspan");m=!m||0===m||1===m?1:m;n=!n||0===n||1===n?1:n;g=0;for(j=a[f];j[g];)g++;o=g;k=1===m?!0:!1;for(j=0;j<m;j++)for(g=0;g<n;g++)a[f+g][o+j]={cell:e,unique:k},a[f+g].nTr=d}e=e.nextSibling}}}function ma(a,b,c){var d=[];c||(c=a.aoHeader,b&&(c=[],aa(c,b)));for(var b=0,e=c.length;b<e;b++)for(var f=0,g=c[b].length;f<
|
||||
g;f++)if(c[b][f].unique&&(!d[f]||!a.bSortCellsTop))d[f]=c[b][f].cell;return d}function na(a,b,c){u(a,"aoServerParams","serverParams",[b]);if(b&&h.isArray(b)){var d={},e=/(.*?)\[\]$/;h.each(b,function(a,b){var c=b.name.match(e);c?(c=c[0],d[c]||(d[c]=[]),d[c].push(b.value)):d[b.name]=b.value});b=d}var f,g=a.ajax,j=a.oInstance;if(h.isPlainObject(g)&&g.data){f=g.data;var i=h.isFunction(f)?f(b):f,b=h.isFunction(f)&&i?i:h.extend(!0,b,i);delete g.data}i={data:b,success:function(b){var d=b.error||b.sError;
|
||||
d&&a.oApi._fnLog(a,0,d);a.json=b;u(a,null,"xhr",[a,b]);c(b)},dataType:"json",cache:!1,type:a.sServerMethod,error:function(b,c){var d=a.oApi._fnLog;"parsererror"==c?d(a,0,"Invalid JSON response",1):4===b.readyState&&d(a,0,"Ajax error",7);B(a,!1)}};a.oAjaxData=b;u(a,null,"preXhr",[a,b]);a.fnServerData?a.fnServerData.call(j,a.sAjaxSource,h.map(b,function(a,b){return{name:b,value:a}}),c,a):a.sAjaxSource||"string"===typeof g?a.jqXHR=h.ajax(h.extend(i,{url:g||a.sAjaxSource})):h.isFunction(g)?a.jqXHR=g.call(j,
|
||||
b,c,a):(a.jqXHR=h.ajax(h.extend(i,g)),g.data=f)}function jb(a){return a.bAjaxDataGet?(a.iDraw++,B(a,!0),na(a,sb(a),function(b){tb(a,b)}),!1):!0}function sb(a){var b=a.aoColumns,c=b.length,d=a.oFeatures,e=a.oPreviousSearch,f=a.aoPreSearchCols,g,j=[],i,o,m,n=R(a);g=a._iDisplayStart;i=!1!==d.bPaginate?a._iDisplayLength:-1;var k=function(a,b){j.push({name:a,value:b})};k("sEcho",a.iDraw);k("iColumns",c);k("sColumns",C(b,"sName").join(","));k("iDisplayStart",g);k("iDisplayLength",i);var l={draw:a.iDraw,
|
||||
columns:[],order:[],start:g,length:i,search:{value:e.sSearch,regex:e.bRegex}};for(g=0;g<c;g++)o=b[g],m=f[g],i="function"==typeof o.mData?"function":o.mData,l.columns.push({data:i,name:o.sName,searchable:o.bSearchable,orderable:o.bSortable,search:{value:m.sSearch,regex:m.bRegex}}),k("mDataProp_"+g,i),d.bFilter&&(k("sSearch_"+g,m.sSearch),k("bRegex_"+g,m.bRegex),k("bSearchable_"+g,o.bSearchable)),d.bSort&&k("bSortable_"+g,o.bSortable);d.bFilter&&(k("sSearch",e.sSearch),k("bRegex",e.bRegex));d.bSort&&
|
||||
(h.each(n,function(a,b){l.order.push({column:b.col,dir:b.dir});k("iSortCol_"+a,b.col);k("sSortDir_"+a,b.dir)}),k("iSortingCols",n.length));b=p.ext.legacy.ajax;return null===b?a.sAjaxSource?j:l:b?j:l}function tb(a,b){var c=b.sEcho!==l?b.sEcho:b.draw,d=b.iTotalRecords!==l?b.iTotalRecords:b.recordsTotal,e=b.iTotalDisplayRecords!==l?b.iTotalDisplayRecords:b.recordsFiltered;if(c){if(1*c<a.iDraw)return;a.iDraw=1*c}ja(a);a._iRecordsTotal=parseInt(d,10);a._iRecordsDisplay=parseInt(e,10);c=oa(a,b);d=0;for(e=
|
||||
c.length;d<e;d++)I(a,c[d]);a.aiDisplay=a.aiDisplayMaster.slice();a.bAjaxDataGet=!1;K(a);a._bInitComplete||pa(a,b);a.bAjaxDataGet=!0;B(a,!1)}function oa(a,b){var c=h.isPlainObject(a.ajax)&&a.ajax.dataSrc!==l?a.ajax.dataSrc:a.sAjaxDataProp;return"data"===c?b.aaData||b[c]:""!==c?U(c)(b):b}function nb(a){var b=a.oClasses,c=a.sTableId,d=a.oLanguage,e=a.oPreviousSearch,f=a.aanFeatures,g='<input type="search" class="'+b.sFilterInput+'"/>',j=d.sSearch,j=j.match(/_INPUT_/)?j.replace("_INPUT_",g):j+g,b=h("<div/>",
|
||||
{id:!f.f?c+"_filter":null,"class":b.sFilter}).append(h("<label/>").append(j)),f=function(){var b=!this.value?"":this.value;b!=e.sSearch&&(ca(a,{sSearch:b,bRegex:e.bRegex,bSmart:e.bSmart,bCaseInsensitive:e.bCaseInsensitive}),a._iDisplayStart=0,K(a))},i=h("input",b).val(e.sSearch).attr("placeholder",d.sSearchPlaceholder).bind("keyup.DT search.DT input.DT paste.DT cut.DT","ssp"===z(a)?Ma(f,400):f).bind("keypress.DT",function(a){if(13==a.keyCode)return!1}).attr("aria-controls",c);h(a.nTable).on("search.dt.DT",
|
||||
function(b,c){if(a===c)try{i[0]!==O.activeElement&&i.val(e.sSearch)}catch(d){}});return b[0]}function ca(a,b,c){var d=a.oPreviousSearch,e=a.aoPreSearchCols,f=function(a){d.sSearch=a.sSearch;d.bRegex=a.bRegex;d.bSmart=a.bSmart;d.bCaseInsensitive=a.bCaseInsensitive};Da(a);if("ssp"!=z(a)){ub(a,b.sSearch,c,b.bEscapeRegex!==l?!b.bEscapeRegex:b.bRegex,b.bSmart,b.bCaseInsensitive);f(b);for(b=0;b<e.length;b++)vb(a,e[b].sSearch,b,e[b].bEscapeRegex!==l?!e[b].bEscapeRegex:e[b].bRegex,e[b].bSmart,e[b].bCaseInsensitive);
|
||||
wb(a)}else f(b);a.bFiltered=!0;u(a,null,"search",[a])}function wb(a){for(var b=p.ext.search,c=a.aiDisplay,d,e,f=0,g=b.length;f<g;f++){for(var j=[],i=0,h=c.length;i<h;i++)e=c[i],d=a.aoData[e],b[f](a,d._aFilterData,e,d._aData,i)&&j.push(e);c.length=0;c.push.apply(c,j)}}function vb(a,b,c,d,e,f){if(""!==b)for(var g=a.aiDisplay,d=Na(b,d,e,f),e=g.length-1;0<=e;e--)b=a.aoData[g[e]]._aFilterData[c],d.test(b)||g.splice(e,1)}function ub(a,b,c,d,e,f){var d=Na(b,d,e,f),e=a.oPreviousSearch.sSearch,f=a.aiDisplayMaster,
|
||||
g;0!==p.ext.search.length&&(c=!0);g=xb(a);if(0>=b.length)a.aiDisplay=f.slice();else{if(g||c||e.length>b.length||0!==b.indexOf(e)||a.bSorted)a.aiDisplay=f.slice();b=a.aiDisplay;for(c=b.length-1;0<=c;c--)d.test(a.aoData[b[c]]._sFilterRow)||b.splice(c,1)}}function Na(a,b,c,d){a=b?a:Oa(a);c&&(a="^(?=.*?"+h.map(a.match(/"[^"]+"|[^ ]+/g)||"",function(a){return'"'===a.charAt(0)?a.match(/^"(.*)"$/)[1]:a}).join(")(?=.*?")+").*$");return RegExp(a,d?"i":"")}function Oa(a){return a.replace(Vb,"\\$1")}function xb(a){var b=
|
||||
a.aoColumns,c,d,e,f,g,j,i,h,m=p.ext.type.search;c=!1;d=0;for(f=a.aoData.length;d<f;d++)if(h=a.aoData[d],!h._aFilterData){j=[];e=0;for(g=b.length;e<g;e++)if(c=b[e],c.bSearchable?(i=A(a,d,e,"filter"),i=m[c.sType]?m[c.sType](i):null!==i?i:""):i="",i)i.indexOf&&-1!==i.indexOf("&")&&(qa.innerHTML=i,i=Wb?qa.textContent:qa.innerText),i.replace&&(i=i.replace(/[\r\n]/g,"")),j.push(i);h._aFilterData=j;h._sFilterRow=j.join(" ");c=!0}return c}function yb(a){return{search:a.sSearch,smart:a.bSmart,regex:a.bRegex,
|
||||
caseInsensitive:a.bCaseInsensitive}}function zb(a){return{sSearch:a.search,bSmart:a.smart,bRegex:a.regex,bCaseInsensitive:a.caseInsensitive}}function qb(a){var b=a.sTableId,c=a.aanFeatures.i,d=h("<div/>",{"class":a.oClasses.sInfo,id:!c?b+"_info":null});c||(a.aoDrawCallback.push({fn:Ab,sName:"information"}),d.attr("role","status").attr("aria-live","polite"),h(a.nTable).attr("aria-describedby",b+"_info"));return d[0]}function Ab(a){var b=a.aanFeatures.i;if(0!==b.length){var c=a.oLanguage,d=a._iDisplayStart+
|
||||
1,e=a.fnDisplayEnd(),f=a.fnRecordsTotal(),g=a.fnRecordsDisplay(),j=g?c.sInfo:c.sInfoEmpty;g!==f&&(j+=" "+c.sInfoFiltered);j+=c.sInfoPostFix;j=Bb(a,j);c=c.fnInfoCallback;null!==c&&(j=c.call(a.oInstance,a,d,e,f,g,j));h(b).html(j)}}function Bb(a,b){var c=a.fnFormatNumber,d=a._iDisplayStart+1,e=a._iDisplayLength,f=a.fnRecordsDisplay(),g=-1===e;return b.replace(/_START_/g,c.call(a,d)).replace(/_END_/g,c.call(a,a.fnDisplayEnd())).replace(/_MAX_/g,c.call(a,a.fnRecordsTotal())).replace(/_TOTAL_/g,c.call(a,
|
||||
f)).replace(/_PAGE_/g,c.call(a,g?1:Math.ceil(d/e))).replace(/_PAGES_/g,c.call(a,g?1:Math.ceil(f/e)))}function ra(a){var b,c,d=a.iInitDisplayStart,e=a.aoColumns,f;c=a.oFeatures;if(a.bInitialised){lb(a);ib(a);ba(a,a.aoHeader);ba(a,a.aoFooter);B(a,!0);c.bAutoWidth&&Ca(a);b=0;for(c=e.length;b<c;b++)f=e[b],f.sWidth&&(f.nTh.style.width=s(f.sWidth));L(a);e=z(a);"ssp"!=e&&("ajax"==e?na(a,[],function(c){var f=oa(a,c);for(b=0;b<f.length;b++)I(a,f[b]);a.iInitDisplayStart=d;L(a);B(a,!1);pa(a,c)},a):(B(a,!1),
|
||||
pa(a)))}else setTimeout(function(){ra(a)},200)}function pa(a,b){a._bInitComplete=!0;b&&V(a);u(a,"aoInitComplete","init",[a,b])}function Pa(a,b){var c=parseInt(b,10);a._iDisplayLength=c;Qa(a);u(a,null,"length",[a,c])}function mb(a){for(var b=a.oClasses,c=a.sTableId,d=a.aLengthMenu,e=h.isArray(d[0]),f=e?d[0]:d,d=e?d[1]:d,e=h("<select/>",{name:c+"_length","aria-controls":c,"class":b.sLengthSelect}),g=0,j=f.length;g<j;g++)e[0][g]=new Option(d[g],f[g]);var i=h("<div><label/></div>").addClass(b.sLength);
|
||||
a.aanFeatures.l||(i[0].id=c+"_length");i.children().append(a.oLanguage.sLengthMenu.replace("_MENU_",e[0].outerHTML));h("select",i).val(a._iDisplayLength).bind("change.DT",function(){Pa(a,h(this).val());K(a)});h(a.nTable).bind("length.dt.DT",function(b,c,d){a===c&&h("select",i).val(d)});return i[0]}function rb(a){var b=a.sPaginationType,c=p.ext.pager[b],d="function"===typeof c,e=function(a){K(a)},b=h("<div/>").addClass(a.oClasses.sPaging+b)[0],f=a.aanFeatures;d||c.fnInit(a,b,e);f.p||(b.id=a.sTableId+
|
||||
"_paginate",a.aoDrawCallback.push({fn:function(a){if(d){var b=a._iDisplayStart,i=a._iDisplayLength,h=a.fnRecordsDisplay(),m=-1===i,b=m?0:Math.ceil(b/i),i=m?1:Math.ceil(h/i),h=c(b,i),n,m=0;for(n=f.p.length;m<n;m++)La(a,"pageButton")(a,f.p[m],m,h,b,i)}else c.fnUpdate(a,e)},sName:"pagination"}));return b}function Ra(a,b,c){var d=a._iDisplayStart,e=a._iDisplayLength,f=a.fnRecordsDisplay();0===f||-1===e?d=0:"number"===typeof b?(d=b*e,d>f&&(d=0)):"first"==b?d=0:"previous"==b?(d=0<=e?d-e:0,0>d&&(d=0)):"next"==
|
||||
b?d+e<f&&(d+=e):"last"==b?d=Math.floor((f-1)/e)*e:P(a,0,"Unknown paging action: "+b,5);b=a._iDisplayStart!==d;a._iDisplayStart=d;b&&(u(a,null,"page",[a]),c&&K(a));return b}function ob(a){return h("<div/>",{id:!a.aanFeatures.r?a.sTableId+"_processing":null,"class":a.oClasses.sProcessing}).html(a.oLanguage.sProcessing).insertBefore(a.nTable)[0]}function B(a,b){a.oFeatures.bProcessing&&h(a.aanFeatures.r).css("display",b?"block":"none");u(a,null,"processing",[a,b])}function pb(a){var b=h(a.nTable);b.attr("role",
|
||||
"grid");var c=a.oScroll;if(""===c.sX&&""===c.sY)return a.nTable;var d=c.sX,e=c.sY,f=a.oClasses,g=b.children("caption"),j=g.length?g[0]._captionSide:null,i=h(b[0].cloneNode(!1)),o=h(b[0].cloneNode(!1)),m=b.children("tfoot");c.sX&&"100%"===b.attr("width")&&b.removeAttr("width");m.length||(m=null);c=h("<div/>",{"class":f.sScrollWrapper}).append(h("<div/>",{"class":f.sScrollHead}).css({overflow:"hidden",position:"relative",border:0,width:d?!d?null:s(d):"100%"}).append(h("<div/>",{"class":f.sScrollHeadInner}).css({"box-sizing":"content-box",
|
||||
width:c.sXInner||"100%"}).append(i.removeAttr("id").css("margin-left",0).append(b.children("thead")))).append("top"===j?g:null)).append(h("<div/>",{"class":f.sScrollBody}).css({overflow:"auto",height:!e?null:s(e),width:!d?null:s(d)}).append(b));m&&c.append(h("<div/>",{"class":f.sScrollFoot}).css({overflow:"hidden",border:0,width:d?!d?null:s(d):"100%"}).append(h("<div/>",{"class":f.sScrollFootInner}).append(o.removeAttr("id").css("margin-left",0).append(b.children("tfoot")))).append("bottom"===j?g:
|
||||
null));var b=c.children(),n=b[0],f=b[1],k=m?b[2]:null;d&&h(f).scroll(function(){var a=this.scrollLeft;n.scrollLeft=a;m&&(k.scrollLeft=a)});a.nScrollHead=n;a.nScrollBody=f;a.nScrollFoot=k;a.aoDrawCallback.push({fn:W,sName:"scrolling"});return c[0]}function W(a){var b=a.oScroll,c=b.sX,d=b.sXInner,e=b.sY,f=b.iBarWidth,g=h(a.nScrollHead),j=g[0].style,i=g.children("div"),o=i[0].style,m=i.children("table"),i=a.nScrollBody,n=h(i),k=i.style,l=h(a.nScrollFoot).children("div"),p=l.children("table"),r=h(a.nTHead),
|
||||
q=h(a.nTable),da=q[0],M=da.style,J=a.nTFoot?h(a.nTFoot):null,u=a.oBrowser,v=u.bScrollOversize,y,t,x,w,z,A=[],B=[],C=[],D,E=function(a){a=a.style;a.paddingTop="0";a.paddingBottom="0";a.borderTopWidth="0";a.borderBottomWidth="0";a.height=0};q.children("thead, tfoot").remove();z=r.clone().prependTo(q);y=r.find("tr");x=z.find("tr");z.find("th, td").removeAttr("tabindex");J&&(w=J.clone().prependTo(q),t=J.find("tr"),w=w.find("tr"));c||(k.width="100%",g[0].style.width="100%");h.each(ma(a,z),function(b,c){D=
|
||||
ga(a,b);c.style.width=a.aoColumns[D].sWidth});J&&F(function(a){a.style.width=""},w);b.bCollapse&&""!==e&&(k.height=n[0].offsetHeight+r[0].offsetHeight+"px");g=q.outerWidth();if(""===c){if(M.width="100%",v&&(q.find("tbody").height()>i.offsetHeight||"scroll"==n.css("overflow-y")))M.width=s(q.outerWidth()-f)}else""!==d?M.width=s(d):g==n.width()&&n.height()<q.height()?(M.width=s(g-f),q.outerWidth()>g-f&&(M.width=s(g))):M.width=s(g);g=q.outerWidth();F(E,x);F(function(a){C.push(a.innerHTML);A.push(s(h(a).css("width")))},
|
||||
x);F(function(a,b){a.style.width=A[b]},y);h(x).height(0);J&&(F(E,w),F(function(a){B.push(s(h(a).css("width")))},w),F(function(a,b){a.style.width=B[b]},t),h(w).height(0));F(function(a,b){a.innerHTML='<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+C[b]+"</div>";a.style.width=A[b]},x);J&&F(function(a,b){a.innerHTML="";a.style.width=B[b]},w);if(q.outerWidth()<g){t=i.scrollHeight>i.offsetHeight||"scroll"==n.css("overflow-y")?g+f:g;if(v&&(i.scrollHeight>i.offsetHeight||"scroll"==n.css("overflow-y")))M.width=
|
||||
s(t-f);(""===c||""!==d)&&P(a,1,"Possible column misalignment",6)}else t="100%";k.width=s(t);j.width=s(t);J&&(a.nScrollFoot.style.width=s(t));!e&&v&&(k.height=s(da.offsetHeight+f));e&&b.bCollapse&&(k.height=s(e),b=c&&da.offsetWidth>i.offsetWidth?f:0,da.offsetHeight<i.offsetHeight&&(k.height=s(da.offsetHeight+b)));b=q.outerWidth();m[0].style.width=s(b);o.width=s(b);m=q.height()>i.clientHeight||"scroll"==n.css("overflow-y");u="padding"+(u.bScrollbarLeft?"Left":"Right");o[u]=m?f+"px":"0px";J&&(p[0].style.width=
|
||||
s(b),l[0].style.width=s(b),l[0].style[u]=m?f+"px":"0px");n.scroll();if((a.bSorted||a.bFiltered)&&!a._drawHold)i.scrollTop=0}function F(a,b,c){for(var d=0,e=0,f=b.length,g,j;e<f;){g=b[e].firstChild;for(j=c?c[e].firstChild:null;g;)1===g.nodeType&&(c?a(g,j,d):a(g,d),d++),g=g.nextSibling,j=c?j.nextSibling:null;e++}}function Ca(a){var b=a.nTable,c=a.aoColumns,d=a.oScroll,e=d.sY,f=d.sX,g=d.sXInner,j=c.length,d=X(a,"bVisible"),i=h("th",a.nTHead),o=b.getAttribute("width"),m=b.parentNode,n=!1,k,l;for(k=0;k<
|
||||
d.length;k++)l=c[d[k]],null!==l.sWidth&&(l.sWidth=Cb(l.sWidthOrig,m),n=!0);if(!n&&!f&&!e&&j==Z(a)&&j==i.length)for(k=0;k<j;k++)c[k].sWidth=s(i.eq(k).width());else{j=h(b).clone().empty().css("visibility","hidden").removeAttr("id").append(h(a.nTHead).clone(!1)).append(h(a.nTFoot).clone(!1)).append(h("<tbody><tr/></tbody>"));j.find("tfoot th, tfoot td").css("width","");var p=j.find("tbody tr"),i=ma(a,j.find("thead")[0]);for(k=0;k<d.length;k++)l=c[d[k]],i[k].style.width=null!==l.sWidthOrig&&""!==l.sWidthOrig?
|
||||
s(l.sWidthOrig):"";if(a.aoData.length)for(k=0;k<d.length;k++)n=d[k],l=c[n],h(Db(a,n)).clone(!1).append(l.sContentPadding).appendTo(p);j.appendTo(m);f&&g?j.width(g):f?(j.css("width","auto"),j.width()<m.offsetWidth&&j.width(m.offsetWidth)):e?j.width(m.offsetWidth):o&&j.width(o);Eb(a,j[0]);if(f){for(k=g=0;k<d.length;k++)l=c[d[k]],e=h(i[k]).outerWidth(),g+=null===l.sWidthOrig?e:parseInt(l.sWidth,10)+e-h(i[k]).width();j.width(s(g));b.style.width=s(g)}for(k=0;k<d.length;k++)if(l=c[d[k]],e=h(i[k]).width())l.sWidth=
|
||||
s(e);b.style.width=s(j.css("width"));j.remove()}o&&(b.style.width=s(o));if((o||f)&&!a._reszEvt)h(za).bind("resize.DT-"+a.sInstance,Ma(function(){V(a)})),a._reszEvt=!0}function Ma(a,b){var c=b||200,d,e;return function(){var b=this,g=+new Date,j=arguments;d&&g<d+c?(clearTimeout(e),e=setTimeout(function(){d=l;a.apply(b,j)},c)):d?(d=g,a.apply(b,j)):d=g}}function Cb(a,b){if(!a)return 0;var c=h("<div/>").css("width",s(a)).appendTo(b||O.body),d=c[0].offsetWidth;c.remove();return d}function Eb(a,b){var c=
|
||||
a.oScroll;if(c.sX||c.sY)c=!c.sX?c.iBarWidth:0,b.style.width=s(h(b).outerWidth()-c)}function Db(a,b){var c=Fb(a,b);if(0>c)return null;var d=a.aoData[c];return!d.nTr?h("<td/>").html(A(a,c,b,"display"))[0]:d.anCells[b]}function Fb(a,b){for(var c,d=-1,e=-1,f=0,g=a.aoData.length;f<g;f++)c=A(a,f,b,"display")+"",c=c.replace(Xb,""),c.length>d&&(d=c.length,e=f);return e}function s(a){return null===a?"0px":"number"==typeof a?0>a?"0px":a+"px":a.match(/\d$/)?a+"px":a}function Gb(){if(!p.__scrollbarWidth){var a=
|
||||
h("<p/>").css({width:"100%",height:200,padding:0})[0],b=h("<div/>").css({position:"absolute",top:0,left:0,width:200,height:150,padding:0,overflow:"hidden",visibility:"hidden"}).append(a).appendTo("body"),c=a.offsetWidth;b.css("overflow","scroll");a=a.offsetWidth;c===a&&(a=b[0].clientWidth);b.remove();p.__scrollbarWidth=c-a}return p.__scrollbarWidth}function R(a){var b,c,d=[],e=a.aoColumns,f,g,j,i;b=a.aaSortingFixed;c=h.isPlainObject(b);var o=[];f=function(a){a.length&&!h.isArray(a[0])?o.push(a):o.push.apply(o,
|
||||
a)};h.isArray(b)&&f(b);c&&b.pre&&f(b.pre);f(a.aaSorting);c&&b.post&&f(b.post);for(a=0;a<o.length;a++){i=o[a][0];f=e[i].aDataSort;b=0;for(c=f.length;b<c;b++)g=f[b],j=e[g].sType||"string",d.push({src:i,col:g,dir:o[a][1],index:o[a][2],type:j,formatter:p.ext.type.order[j+"-pre"]})}return d}function kb(a){var b,c,d=[],e=p.ext.type.order,f=a.aoData,g=0,j,i=a.aiDisplayMaster,h;Da(a);h=R(a);b=0;for(c=h.length;b<c;b++)j=h[b],j.formatter&&g++,Hb(a,j.col);if("ssp"!=z(a)&&0!==h.length){b=0;for(c=i.length;b<c;b++)d[i[b]]=
|
||||
b;g===h.length?i.sort(function(a,b){var c,e,g,j,i=h.length,l=f[a]._aSortData,p=f[b]._aSortData;for(g=0;g<i;g++)if(j=h[g],c=l[j.col],e=p[j.col],c=c<e?-1:c>e?1:0,0!==c)return"asc"===j.dir?c:-c;c=d[a];e=d[b];return c<e?-1:c>e?1:0}):i.sort(function(a,b){var c,g,j,i,l=h.length,p=f[a]._aSortData,r=f[b]._aSortData;for(j=0;j<l;j++)if(i=h[j],c=p[i.col],g=r[i.col],i=e[i.type+"-"+i.dir]||e["string-"+i.dir],c=i(c,g),0!==c)return c;c=d[a];g=d[b];return c<g?-1:c>g?1:0})}a.bSorted=!0}function Ib(a){for(var b,c,
|
||||
d=a.aoColumns,e=R(a),a=a.oLanguage.oAria,f=0,g=d.length;f<g;f++){c=d[f];var j=c.asSorting;b=c.sTitle.replace(/<.*?>/g,"");var i=c.nTh;i.removeAttribute("aria-sort");c.bSortable&&(0<e.length&&e[0].col==f?(i.setAttribute("aria-sort","asc"==e[0].dir?"ascending":"descending"),c=j[e[0].index+1]||j[0]):c=j[0],b+="asc"===c?a.sSortAscending:a.sSortDescending);i.setAttribute("aria-label",b)}}function Sa(a,b,c,d){var e=a.aaSorting,f=a.aoColumns[b].asSorting,g=function(a){var b=a._idx;b===l&&(b=h.inArray(a[1],
|
||||
f));return b+1>=f.length?0:b+1};"number"===typeof e[0]&&(e=a.aaSorting=[e]);c&&a.oFeatures.bSortMulti?(c=h.inArray(b,C(e,"0")),-1!==c?(b=g(e[c]),e[c][1]=f[b],e[c]._idx=b):(e.push([b,f[0],0]),e[e.length-1]._idx=0)):e.length&&e[0][0]==b?(b=g(e[0]),e.length=1,e[0][1]=f[b],e[0]._idx=b):(e.length=0,e.push([b,f[0]]),e[0]._idx=0);L(a);"function"==typeof d&&d(a)}function Ka(a,b,c,d){var e=a.aoColumns[c];Ta(b,{},function(b){!1!==e.bSortable&&(a.oFeatures.bProcessing?(B(a,!0),setTimeout(function(){Sa(a,c,b.shiftKey,
|
||||
d);"ssp"!==z(a)&&B(a,!1)},0)):Sa(a,c,b.shiftKey,d))})}function sa(a){var b=a.aLastSort,c=a.oClasses.sSortColumn,d=R(a),e=a.oFeatures,f,g;if(e.bSort&&e.bSortClasses){e=0;for(f=b.length;e<f;e++)g=b[e].src,h(C(a.aoData,"anCells",g)).removeClass(c+(2>e?e+1:3));e=0;for(f=d.length;e<f;e++)g=d[e].src,h(C(a.aoData,"anCells",g)).addClass(c+(2>e?e+1:3))}a.aLastSort=d}function Hb(a,b){var c=a.aoColumns[b],d=p.ext.order[c.sSortDataType],e;d&&(e=d.call(a.oInstance,a,b,Y(a,b)));for(var f,g=p.ext.type.order[c.sType+
|
||||
"-pre"],j=0,i=a.aoData.length;j<i;j++)if(c=a.aoData[j],c._aSortData||(c._aSortData=[]),!c._aSortData[b]||d)f=d?e[j]:A(a,j,b,"sort"),c._aSortData[b]=g?g(f):f}function ta(a){if(a.oFeatures.bStateSave&&!a.bDestroying){var b={time:+new Date,start:a._iDisplayStart,length:a._iDisplayLength,order:h.extend(!0,[],a.aaSorting),search:yb(a.oPreviousSearch),columns:h.map(a.aoColumns,function(b,d){return{visible:b.bVisible,search:yb(a.aoPreSearchCols[d])}})};u(a,"aoStateSaveParams","stateSaveParams",[a,b]);a.oSavedState=
|
||||
b;a.fnStateSaveCallback.call(a.oInstance,a,b)}}function Jb(a){var b,c,d=a.aoColumns;if(a.oFeatures.bStateSave){var e=a.fnStateLoadCallback.call(a.oInstance,a);if(e&&e.time&&(b=u(a,"aoStateLoadParams","stateLoadParams",[a,e]),-1===h.inArray(!1,b)&&(b=a.iStateDuration,!(0<b&&e.time<+new Date-1E3*b)&&d.length===e.columns.length))){a.oLoadedState=h.extend(!0,{},e);a._iDisplayStart=e.start;a.iInitDisplayStart=e.start;a._iDisplayLength=e.length;a.aaSorting=[];h.each(e.order,function(b,c){a.aaSorting.push(c[0]>=
|
||||
d.length?[0,c[1]]:c)});h.extend(a.oPreviousSearch,zb(e.search));b=0;for(c=e.columns.length;b<c;b++){var f=e.columns[b];d[b].bVisible=f.visible;h.extend(a.aoPreSearchCols[b],zb(f.search))}u(a,"aoStateLoaded","stateLoaded",[a,e])}}}function ua(a){var b=p.settings,a=h.inArray(a,C(b,"nTable"));return-1!==a?b[a]:null}function P(a,b,c,d){c="DataTables warning: "+(null!==a?"table id="+a.sTableId+" - ":"")+c;d&&(c+=". For more information about this error, please see http://datatables.net/tn/"+d);if(b)za.console&&
|
||||
console.log&&console.log(c);else if(a=p.ext,"alert"==(a.sErrMode||a.errMode))alert(c);else throw Error(c);}function D(a,b,c,d){h.isArray(c)?h.each(c,function(c,d){h.isArray(d)?D(a,b,d[0],d[1]):D(a,b,d)}):(d===l&&(d=c),b[c]!==l&&(a[d]=b[c]))}function Kb(a,b,c){var d,e;for(e in b)b.hasOwnProperty(e)&&(d=b[e],h.isPlainObject(d)?(h.isPlainObject(a[e])||(a[e]={}),h.extend(!0,a[e],d)):a[e]=c&&"data"!==e&&"aaData"!==e&&h.isArray(d)?d.slice():d);return a}function Ta(a,b,c){h(a).bind("click.DT",b,function(b){a.blur();
|
||||
c(b)}).bind("keypress.DT",b,function(a){13===a.which&&(a.preventDefault(),c(a))}).bind("selectstart.DT",function(){return!1})}function x(a,b,c,d){c&&a[b].push({fn:c,sName:d})}function u(a,b,c,d){var e=[];b&&(e=h.map(a[b].slice().reverse(),function(b){return b.fn.apply(a.oInstance,d)}));null!==c&&h(a.nTable).trigger(c+".dt",d);return e}function Qa(a){var b=a._iDisplayStart,c=a.fnDisplayEnd(),d=a._iDisplayLength;c===a.fnRecordsDisplay()&&(b=c-d);if(-1===d||0>b)b=0;a._iDisplayStart=b}function La(a,b){var c=
|
||||
a.renderer,d=p.ext.renderer[b];return h.isPlainObject(c)&&c[b]?d[c[b]]||d._:"string"===typeof c?d[c]||d._:d._}function z(a){return a.oFeatures.bServerSide?"ssp":a.ajax||a.sAjaxSource?"ajax":"dom"}function Ua(a,b){var c=[],c=Lb.numbers_length,d=Math.floor(c/2);b<=c?c=S(0,b):a<=d?(c=S(0,c-2),c.push("ellipsis"),c.push(b-1)):(a>=b-1-d?c=S(b-(c-2),b):(c=S(a-1,a+2),c.push("ellipsis"),c.push(b-1)),c.splice(0,0,"ellipsis"),c.splice(0,0,0));c.DT_el="span";return c}function cb(a){h.each({num:function(b){return va(b,
|
||||
a)},"num-fmt":function(b){return va(b,a,Va)},"html-num":function(b){return va(b,a,wa)},"html-num-fmt":function(b){return va(b,a,wa,Va)}},function(b,c){t.type.order[b+a+"-pre"]=c})}function Mb(a){return function(){var b=[ua(this[p.ext.iApiIndex])].concat(Array.prototype.slice.call(arguments));return p.ext.internal[a].apply(this,b)}}var p,t,q,r,v,Wa={},Nb=/[\r\n]/g,wa=/<.*?>/g,Yb=/^[\w\+\-]/,Zb=/[\w\+\-]$/,Vb=RegExp("(\\/|\\.|\\*|\\+|\\?|\\||\\(|\\)|\\[|\\]|\\{|\\}|\\\\|\\$|\\^|\\-)","g"),Va=/[',$\u00a3\u20ac\u00a5%\u2009\u202F]/g,
|
||||
H=function(a){return!a||!0===a||"-"===a?!0:!1},Ob=function(a){var b=parseInt(a,10);return!isNaN(b)&&isFinite(a)?b:null},Pb=function(a,b){Wa[b]||(Wa[b]=RegExp(Oa(b),"g"));return"string"===typeof a?a.replace(/\./g,"").replace(Wa[b],"."):a},Xa=function(a,b,c){var d="string"===typeof a;b&&d&&(a=Pb(a,b));c&&d&&(a=a.replace(Va,""));return H(a)||!isNaN(parseFloat(a))&&isFinite(a)},Qb=function(a,b,c){return H(a)?!0:!(H(a)||"string"===typeof a)?null:Xa(a.replace(wa,""),b,c)?!0:null},C=function(a,b,c){var d=
|
||||
[],e=0,f=a.length;if(c!==l)for(;e<f;e++)a[e]&&a[e][b]&&d.push(a[e][b][c]);else for(;e<f;e++)a[e]&&d.push(a[e][b]);return d},xa=function(a,b,c,d){var e=[],f=0,g=b.length;if(d!==l)for(;f<g;f++)e.push(a[b[f]][c][d]);else for(;f<g;f++)e.push(a[b[f]][c]);return e},S=function(a,b){var c=[],d;b===l?(b=0,d=a):(d=b,b=a);for(var e=b;e<d;e++)c.push(e);return c},Ja=function(a){var b=[],c,d,e=a.length,f,g=0;d=0;a:for(;d<e;d++){c=a[d];for(f=0;f<g;f++)if(b[f]===c)continue a;b.push(c);g++}return b},w=function(a,
|
||||
b,c){a[b]!==l&&(a[c]=a[b])},$=/\[.*?\]$/,Q=/\(\)$/,qa=h("<div>")[0],Wb=qa.textContent!==l,Xb=/<.*?>/g;p=function(a){this.$=function(a,b){return this.api(!0).$(a,b)};this._=function(a,b){return this.api(!0).rows(a,b).data()};this.api=function(a){return a?new q(ua(this[t.iApiIndex])):new q(this)};this.fnAddData=function(a,b){var c=this.api(!0),d=h.isArray(a)&&(h.isArray(a[0])||h.isPlainObject(a[0]))?c.rows.add(a):c.row.add(a);(b===l||b)&&c.draw();return d.flatten().toArray()};this.fnAdjustColumnSizing=
|
||||
function(a){var b=this.api(!0).columns.adjust(),c=b.settings()[0],d=c.oScroll;a===l||a?b.draw(!1):(""!==d.sX||""!==d.sY)&&W(c)};this.fnClearTable=function(a){var b=this.api(!0).clear();(a===l||a)&&b.draw()};this.fnClose=function(a){this.api(!0).row(a).child.hide()};this.fnDeleteRow=function(a,b,c){var d=this.api(!0),a=d.rows(a),e=a.settings()[0],h=e.aoData[a[0][0]];a.remove();b&&b.call(this,e,h);(c===l||c)&&d.draw();return h};this.fnDestroy=function(a){this.api(!0).destroy(a)};this.fnDraw=function(a){this.api(!0).draw(!a)};
|
||||
this.fnFilter=function(a,b,c,d,e,h){e=this.api(!0);null===b||b===l?e.search(a,c,d,h):e.column(b).search(a,c,d,h);e.draw()};this.fnGetData=function(a,b){var c=this.api(!0);if(a!==l){var d=a.nodeName?a.nodeName.toLowerCase():"";return b!==l||"td"==d||"th"==d?c.cell(a,b).data():c.row(a).data()||null}return c.data().toArray()};this.fnGetNodes=function(a){var b=this.api(!0);return a!==l?b.row(a).node():b.rows().nodes().flatten().toArray()};this.fnGetPosition=function(a){var b=this.api(!0),c=a.nodeName.toUpperCase();
|
||||
return"TR"==c?b.row(a).index():"TD"==c||"TH"==c?(a=b.cell(a).index(),[a.row,a.columnVisible,a.column]):null};this.fnIsOpen=function(a){return this.api(!0).row(a).child.isShown()};this.fnOpen=function(a,b,c){return this.api(!0).row(a).child(b,c).show().child()[0]};this.fnPageChange=function(a,b){var c=this.api(!0).page(a);(b===l||b)&&c.draw(!1)};this.fnSetColumnVis=function(a,b,c){a=this.api(!0).column(a).visible(b);(c===l||c)&&a.columns.adjust().draw()};this.fnSettings=function(){return ua(this[t.iApiIndex])};
|
||||
this.fnSort=function(a){this.api(!0).order(a).draw()};this.fnSortListener=function(a,b,c){this.api(!0).order.listener(a,b,c)};this.fnUpdate=function(a,b,c,d,e){var h=this.api(!0);c===l||null===c?h.row(b).data(a):h.cell(b,c).data(a);(e===l||e)&&h.columns.adjust();(d===l||d)&&h.draw();return 0};this.fnVersionCheck=t.fnVersionCheck;var b=this,c=a===l,d=this.length;c&&(a={});this.oApi=this.internal=t.internal;for(var e in p.ext.internal)e&&(this[e]=Mb(e));this.each(function(){var e={},g=1<d?Kb(e,a,!0):
|
||||
a,j=0,i,o=this.getAttribute("id"),e=!1,m=p.defaults;if("table"!=this.nodeName.toLowerCase())P(null,0,"Non-table node initialisation ("+this.nodeName+")",2);else{db(m);eb(m.column);G(m,m,!0);G(m.column,m.column,!0);G(m,g);var n=p.settings,j=0;for(i=n.length;j<i;j++){if(n[j].nTable==this){i=g.bRetrieve!==l?g.bRetrieve:m.bRetrieve;if(c||i)return n[j].oInstance;if(g.bDestroy!==l?g.bDestroy:m.bDestroy){n[j].oInstance.fnDestroy();break}else{P(n[j],0,"Cannot reinitialise DataTable",3);return}}if(n[j].sTableId==
|
||||
this.id){n.splice(j,1);break}}if(null===o||""===o)this.id=o="DataTables_Table_"+p.ext._unique++;var k=h.extend(!0,{},p.models.oSettings,{nTable:this,oApi:b.internal,oInit:g,sDestroyWidth:h(this)[0].style.width,sInstance:o,sTableId:o});n.push(k);k.oInstance=1===b.length?b:h(this).dataTable();db(g);g.oLanguage&&N(g.oLanguage);g.aLengthMenu&&!g.iDisplayLength&&(g.iDisplayLength=h.isArray(g.aLengthMenu[0])?g.aLengthMenu[0][0]:g.aLengthMenu[0]);g=Kb(h.extend(!0,{},m),g);D(k.oFeatures,g,"bPaginate bLengthChange bFilter bSort bSortMulti bInfo bProcessing bAutoWidth bSortClasses bServerSide bDeferRender".split(" "));
|
||||
D(k,g,["asStripeClasses","ajax","fnServerData","fnFormatNumber","sServerMethod","aaSorting","aaSortingFixed","aLengthMenu","sPaginationType","sAjaxSource","sAjaxDataProp","iStateDuration","sDom","bSortCellsTop","iTabIndex","fnStateLoadCallback","fnStateSaveCallback","renderer",["iCookieDuration","iStateDuration"],["oSearch","oPreviousSearch"],["aoSearchCols","aoPreSearchCols"],["iDisplayLength","_iDisplayLength"],["bJQueryUI","bJUI"]]);D(k.oScroll,g,[["sScrollX","sX"],["sScrollXInner","sXInner"],
|
||||
["sScrollY","sY"],["bScrollCollapse","bCollapse"]]);D(k.oLanguage,g,"fnInfoCallback");x(k,"aoDrawCallback",g.fnDrawCallback,"user");x(k,"aoServerParams",g.fnServerParams,"user");x(k,"aoStateSaveParams",g.fnStateSaveParams,"user");x(k,"aoStateLoadParams",g.fnStateLoadParams,"user");x(k,"aoStateLoaded",g.fnStateLoaded,"user");x(k,"aoRowCallback",g.fnRowCallback,"user");x(k,"aoRowCreatedCallback",g.fnCreatedRow,"user");x(k,"aoHeaderCallback",g.fnHeaderCallback,"user");x(k,"aoFooterCallback",g.fnFooterCallback,
|
||||
"user");x(k,"aoInitComplete",g.fnInitComplete,"user");x(k,"aoPreDrawCallback",g.fnPreDrawCallback,"user");o=k.oClasses;g.bJQueryUI?(h.extend(o,p.ext.oJUIClasses,g.oClasses),g.sDom===m.sDom&&"lfrtip"===m.sDom&&(k.sDom='<"H"lfr>t<"F"ip>'),k.renderer)?h.isPlainObject(k.renderer)&&!k.renderer.header&&(k.renderer.header="jqueryui"):k.renderer="jqueryui":h.extend(o,p.ext.classes,g.oClasses);h(this).addClass(o.sTable);if(""!==k.oScroll.sX||""!==k.oScroll.sY)k.oScroll.iBarWidth=Gb();!0===k.oScroll.sX&&(k.oScroll.sX=
|
||||
"100%");k.iInitDisplayStart===l&&(k.iInitDisplayStart=g.iDisplayStart,k._iDisplayStart=g.iDisplayStart);null!==g.iDeferLoading&&(k.bDeferLoading=!0,j=h.isArray(g.iDeferLoading),k._iRecordsDisplay=j?g.iDeferLoading[0]:g.iDeferLoading,k._iRecordsTotal=j?g.iDeferLoading[1]:g.iDeferLoading);""!==g.oLanguage.sUrl?(k.oLanguage.sUrl=g.oLanguage.sUrl,h.getJSON(k.oLanguage.sUrl,null,function(a){N(a);G(m.oLanguage,a);h.extend(true,k.oLanguage,g.oLanguage,a);ra(k)}),e=!0):h.extend(!0,k.oLanguage,g.oLanguage);
|
||||
null===g.asStripeClasses&&(k.asStripeClasses=[o.sStripeOdd,o.sStripeEven]);var j=k.asStripeClasses,r=h("tbody tr:eq(0)",this);-1!==h.inArray(!0,h.map(j,function(a){return r.hasClass(a)}))&&(h("tbody tr",this).removeClass(j.join(" ")),k.asDestroyStripes=j.slice());var n=[],q,j=this.getElementsByTagName("thead");0!==j.length&&(aa(k.aoHeader,j[0]),n=ma(k));if(null===g.aoColumns){q=[];j=0;for(i=n.length;j<i;j++)q.push(null)}else q=g.aoColumns;j=0;for(i=q.length;j<i;j++)Aa(k,n?n[j]:null);hb(k,g.aoColumnDefs,
|
||||
q,function(a,b){fa(k,a,b)});if(r.length){var s=function(a,b){return a.getAttribute("data-"+b)?b:null};h.each(ia(k,r[0]).cells,function(a,b){var c=k.aoColumns[a];if(c.mData===a){var d=s(b,"sort")||s(b,"order"),e=s(b,"filter")||s(b,"search");if(d!==null||e!==null){c.mData={_:a+".display",sort:d!==null?a+".@data-"+d:l,type:d!==null?a+".@data-"+d:l,filter:e!==null?a+".@data-"+e:l};fa(k,a)}}})}var t=k.oFeatures;g.bStateSave&&(t.bStateSave=!0,Jb(k,g),x(k,"aoDrawCallback",ta,"state_save"));if(g.aaSorting===
|
||||
l){n=k.aaSorting;j=0;for(i=n.length;j<i;j++)n[j][1]=k.aoColumns[j].asSorting[0]}sa(k);t.bSort&&x(k,"aoDrawCallback",function(){if(k.bSorted){var a=R(k),b={};h.each(a,function(a,c){b[c.src]=c.dir});u(k,null,"order",[k,a,b]);Ib(k)}});x(k,"aoDrawCallback",function(){(k.bSorted||z(k)==="ssp"||t.bDeferRender)&&sa(k)},"sc");fb(k);j=h(this).children("caption").each(function(){this._captionSide=h(this).css("caption-side")});i=h(this).children("thead");0===i.length&&(i=h("<thead/>").appendTo(this));k.nTHead=
|
||||
i[0];i=h(this).children("tbody");0===i.length&&(i=h("<tbody/>").appendTo(this));k.nTBody=i[0];i=h(this).children("tfoot");if(0===i.length&&0<j.length&&(""!==k.oScroll.sX||""!==k.oScroll.sY))i=h("<tfoot/>").appendTo(this);0===i.length||0===i.children().length?h(this).addClass(o.sNoFooter):0<i.length&&(k.nTFoot=i[0],aa(k.aoFooter,k.nTFoot));if(g.aaData)for(j=0;j<g.aaData.length;j++)I(k,g.aaData[j]);else(k.bDeferLoading||"dom"==z(k))&&ha(k,h(k.nTBody).children("tr"));k.aiDisplay=k.aiDisplayMaster.slice();
|
||||
k.bInitialised=!0;!1===e&&ra(k)}});b=null;return this};var Rb=[],y=Array.prototype,$b=function(a){var b,c,d=p.settings,e=h.map(d,function(a){return a.nTable});if(a){if(a.nTable&&a.oApi)return[a];if(a.nodeName&&"table"===a.nodeName.toLowerCase())return b=h.inArray(a,e),-1!==b?[d[b]]:null;if(a&&"function"===typeof a.settings)return a.settings().toArray();"string"===typeof a?c=h(a):a instanceof h&&(c=a)}else return[];if(c)return c.map(function(){b=h.inArray(this,e);return-1!==b?d[b]:null}).toArray()};
|
||||
q=function(a,b){if(!this instanceof q)throw"DT API must be constructed as a new object";var c=[],d=function(a){(a=$b(a))&&c.push.apply(c,a)};if(h.isArray(a))for(var e=0,f=a.length;e<f;e++)d(a[e]);else d(a);this.context=Ja(c);b&&this.push.apply(this,b.toArray?b.toArray():b);this.selector={rows:null,cols:null,opts:null};q.extend(this,this,Rb)};p.Api=q;q.prototype={concat:y.concat,context:[],each:function(a){for(var b=0,c=this.length;b<c;b++)a.call(this,this[b],b,this);return this},eq:function(a){var b=
|
||||
this.context;return b.length>a?new q(b[a],this[a]):null},filter:function(a){var b=[];if(y.filter)b=y.filter.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)a.call(this,this[c],c,this)&&b.push(this[c]);return new q(this.context,b)},flatten:function(){var a=[];return new q(this.context,a.concat.apply(a,this.toArray()))},join:y.join,indexOf:y.indexOf||function(a,b){for(var c=b||0,d=this.length;c<d;c++)if(this[c]===a)return c;return-1},iterator:function(a,b,c){var d=[],e,f,g,h,i,o=this.context,
|
||||
m,n,k=this.selector;"string"===typeof a&&(c=b,b=a,a=!1);f=0;for(g=o.length;f<g;f++)if("table"===b)e=c(o[f],f),e!==l&&d.push(e);else if("columns"===b||"rows"===b)e=c(o[f],this[f],f),e!==l&&d.push(e);else if("column"===b||"column-rows"===b||"row"===b||"cell"===b){n=this[f];"column-rows"===b&&(m=Ya(o[f],k.opts));h=0;for(i=n.length;h<i;h++)e=n[h],e="cell"===b?c(o[f],e.row,e.column,f,h):c(o[f],e,f,h,m),e!==l&&d.push(e)}return d.length?(a=new q(o,a?d.concat.apply([],d):d),b=a.selector,b.rows=k.rows,b.cols=
|
||||
k.cols,b.opts=k.opts,a):this},lastIndexOf:y.lastIndexOf||function(a,b){return this.indexOf.apply(this.toArray.reverse(),arguments)},length:0,map:function(a){var b=[];if(y.map)b=y.map.call(this,a,this);else for(var c=0,d=this.length;c<d;c++)b.push(a.call(this,this[c],c));return new q(this.context,b)},pluck:function(a){return this.map(function(b){return b[a]})},pop:y.pop,push:y.push,reduce:y.reduce||function(a,b){return gb(this,a,b,0,this.length,1)},reduceRight:y.reduceRight||function(a,b){return gb(this,
|
||||
a,b,this.length-1,-1,-1)},reverse:y.reverse,selector:null,shift:y.shift,sort:y.sort,splice:y.splice,toArray:function(){return y.slice.call(this)},to$:function(){return h(this)},toJQuery:function(){return h(this)},unique:function(){return new q(this.context,Ja(this))},unshift:y.unshift};q.extend=function(a,b,c){if(b&&(b instanceof q||b.__dt_wrapper)){var d,e,f,g=function(a,b,c){return function(){var d=b.apply(a,arguments);q.extend(d,d,c.methodExt);return d}};d=0;for(e=c.length;d<e;d++)f=c[d],b[f.name]=
|
||||
"function"===typeof f.val?g(a,f.val,f):h.isPlainObject(f.val)?{}:f.val,b[f.name].__dt_wrapper=!0,q.extend(a,b[f.name],f.propExt)}};q.register=r=function(a,b){if(h.isArray(a))for(var c=0,d=a.length;c<d;c++)q.register(a[c],b);else for(var e=a.split("."),f=Rb,g,j,c=0,d=e.length;c<d;c++){g=(j=-1!==e[c].indexOf("()"))?e[c].replace("()",""):e[c];var i;a:{i=0;for(var o=f.length;i<o;i++)if(f[i].name===g){i=f[i];break a}i=null}i||(i={name:g,val:{},methodExt:[],propExt:[]},f.push(i));c===d-1?i.val=b:f=j?i.methodExt:
|
||||
i.propExt}};q.registerPlural=v=function(a,b,c){q.register(a,c);q.register(b,function(){var a=c.apply(this,arguments);return a===this?this:a instanceof q?a.length?h.isArray(a[0])?new q(a.context,a[0]):a[0]:l:a})};r("tables()",function(a){var b;if(a){b=q;var c=this.context;if("number"===typeof a)a=[c[a]];else var d=h.map(c,function(a){return a.nTable}),a=h(d).filter(a).map(function(){var a=h.inArray(this,d);return c[a]}).toArray();b=new b(a)}else b=this;return b});r("table()",function(a){var a=this.tables(a),
|
||||
b=a.context;return b.length?new q(b[0]):a});v("tables().nodes()","table().node()",function(){return this.iterator("table",function(a){return a.nTable})});v("tables().body()","table().body()",function(){return this.iterator("table",function(a){return a.nTBody})});v("tables().header()","table().header()",function(){return this.iterator("table",function(a){return a.nTHead})});v("tables().footer()","table().footer()",function(){return this.iterator("table",function(a){return a.nTFoot})});v("tables().containers()",
|
||||
"table().container()",function(){return this.iterator("table",function(a){return a.nTableWrapper})});r("draw()",function(a){return this.iterator("table",function(b){L(b,!1===a)})});r("page()",function(a){return a===l?this.page.info().page:this.iterator("table",function(b){Ra(b,a)})});r("page.info()",function(){if(0===this.context.length)return l;var a=this.context[0],b=a._iDisplayStart,c=a._iDisplayLength,d=a.fnRecordsDisplay(),e=-1===c;return{page:e?0:Math.floor(b/c),pages:e?1:Math.ceil(d/c),start:b,
|
||||
end:a.fnDisplayEnd(),length:c,recordsTotal:a.fnRecordsTotal(),recordsDisplay:d}});r("page.len()",function(a){return a===l?0!==this.context.length?this.context[0]._iDisplayLength:l:this.iterator("table",function(b){Pa(b,a)})});var Sb=function(a,b,c){"ssp"==z(a)?L(a,b):(B(a,!0),na(a,[],function(c){ja(a);for(var c=oa(a,c),d=0,g=c.length;d<g;d++)I(a,c[d]);L(a,b);B(a,!1)}));if(c){var d=new q(a);d.one("draw",function(){c(d.ajax.json())})}};r("ajax.json()",function(){var a=this.context;if(0<a.length)return a[0].json});
|
||||
r("ajax.params()",function(){var a=this.context;if(0<a.length)return a[0].oAjaxData});r("ajax.reload()",function(a,b){return this.iterator("table",function(c){Sb(c,!1===b,a)})});r("ajax.url()",function(a){var b=this.context;if(a===l){if(0===b.length)return l;b=b[0];return b.ajax?h.isPlainObject(b.ajax)?b.ajax.url:b.ajax:b.sAjaxSource}return this.iterator("table",function(b){h.isPlainObject(b.ajax)?b.ajax.url=a:b.ajax=a})});r("ajax.url().load()",function(a,b){return this.iterator("table",function(c){Sb(c,
|
||||
!1===b,a)})});var Za=function(a,b){var c=[],d,e,f,g,j,i;if(!a||"string"===typeof a||a.length===l)a=[a];f=0;for(g=a.length;f<g;f++){e=a[f]&&a[f].split?a[f].split(","):[a[f]];j=0;for(i=e.length;j<i;j++)(d=b("string"===typeof e[j]?h.trim(e[j]):e[j]))&&d.length&&c.push.apply(c,d)}return c},$a=function(a){a||(a={});a.filter&&!a.search&&(a.search=a.filter);return{search:a.search||"none",order:a.order||"current",page:a.page||"all"}},ab=function(a){for(var b=0,c=a.length;b<c;b++)if(0<a[b].length)return a[0]=
|
||||
a[b],a.length=1,a.context=[a.context[b]],a;a.length=0;return a},Ya=function(a,b){var c,d,e,f=[],g=a.aiDisplay;c=a.aiDisplayMaster;var j=b.search;d=b.order;e=b.page;if("ssp"==z(a))return"removed"===j?[]:S(0,c.length);if("current"==e){c=a._iDisplayStart;for(d=a.fnDisplayEnd();c<d;c++)f.push(g[c])}else if("current"==d||"applied"==d)f="none"==j?c.slice():"applied"==j?g.slice():h.map(c,function(a){return-1===h.inArray(a,g)?a:null});else if("index"==d||"original"==d){c=0;for(d=a.aoData.length;c<d;c++)"none"==
|
||||
j?f.push(c):(e=h.inArray(c,g),(-1===e&&"removed"==j||0<=e&&"applied"==j)&&f.push(c))}return f};r("rows()",function(a,b){a===l?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(c){var e=b;return Za(a,function(a){var b=Ob(a);if(b!==null&&!e)return[b];var j=Ya(c,e);if(b!==null&&h.inArray(b,j)!==-1)return[b];if(!a)return j;for(var b=[],i=0,o=j.length;i<o;i++)b.push(c.aoData[j[i]].nTr);return a.nodeName&&h.inArray(a,b)!==-1?[a._DT_RowIndex]:h(b).filter(a).map(function(){return this._DT_RowIndex}).toArray()})});
|
||||
c.selector.rows=a;c.selector.opts=b;return c});r("rows().nodes()",function(){return this.iterator("row",function(a,b){return a.aoData[b].nTr||l})});r("rows().data()",function(){return this.iterator(!0,"rows",function(a,b){return xa(a.aoData,b,"_aData")})});v("rows().cache()","row().cache()",function(a){return this.iterator("row",function(b,c){var d=b.aoData[c];return"search"===a?d._aFilterData:d._aSortData})});v("rows().invalidate()","row().invalidate()",function(a){return this.iterator("row",function(b,
|
||||
c){la(b,c,a)})});v("rows().indexes()","row().index()",function(){return this.iterator("row",function(a,b){return b})});v("rows().remove()","row().remove()",function(){var a=this;return this.iterator("row",function(b,c,d){var e=b.aoData;e.splice(c,1);for(var f=0,g=e.length;f<g;f++)null!==e[f].nTr&&(e[f].nTr._DT_RowIndex=f);h.inArray(c,b.aiDisplay);ka(b.aiDisplayMaster,c);ka(b.aiDisplay,c);ka(a[d],c,!1);Qa(b)})});r("rows.add()",function(a){var b=this.iterator("table",function(b){var c,f,g,h=[];f=0;
|
||||
for(g=a.length;f<g;f++)c=a[f],c.nodeName&&"TR"===c.nodeName.toUpperCase()?h.push(ha(b,c)[0]):h.push(I(b,c));return h}),c=this.rows(-1);c.pop();c.push.apply(c,b.toArray());return c});r("row()",function(a,b){return ab(this.rows(a,b))});r("row().data()",function(a){var b=this.context;if(a===l)return b.length&&this.length?b[0].aoData[this[0]]._aData:l;b[0].aoData[this[0]]._aData=a;la(b[0],this[0],"data");return this});r("row().node()",function(){var a=this.context;return a.length&&this.length?a[0].aoData[this[0]].nTr||
|
||||
null:null});r("row.add()",function(a){a instanceof h&&a.length&&(a=a[0]);var b=this.iterator("table",function(b){return a.nodeName&&"TR"===a.nodeName.toUpperCase()?ha(b,a)[0]:I(b,a)});return this.row(b[0])});var bb=function(a){var b=a.context;b.length&&a.length&&(a=b[0].aoData[a[0]],a._details&&(a._details.remove(),a._detailsShow=l,a._details=l))},Tb=function(a,b){var c=a.context;if(c.length&&a.length){var d=c[0].aoData[a[0]];if(d._details){(d._detailsShow=b)?d._details.insertAfter(d.nTr):d._details.detach();
|
||||
var e=c[0],f=new q(e),g=e.aoData;f.off("draw.dt.DT_details column-visibility.dt.DT_details destroy.dt.DT_details");0<C(g,"_details").length&&(f.on("draw.dt.DT_details",function(a,b){e===b&&f.rows({page:"current"}).eq(0).each(function(a){a=g[a];a._detailsShow&&a._details.insertAfter(a.nTr)})}),f.on("column-visibility.dt.DT_details",function(a,b){if(e===b)for(var c,d=Z(b),f=0,h=g.length;f<h;f++)c=g[f],c._details&&c._details.children("td[colspan]").attr("colspan",d)}),f.on("destroy.dt.DT_details",function(a,
|
||||
b){if(e===b)for(var c=0,d=g.length;c<d;c++)g[c]._details&&bb(g[c])}))}}};r("row().child()",function(a,b){var c=this.context;if(a===l)return c.length&&this.length?c[0].aoData[this[0]]._details:l;if(!0===a)this.child.show();else if(!1===a)bb(this);else if(c.length&&this.length){var d=c[0],c=c[0].aoData[this[0]],e=[],f=function(a,b){if(a.nodeName&&"tr"===a.nodeName.toLowerCase())e.push(a);else{var c=h("<tr><td/></tr>").addClass(b);h("td",c).addClass(b).html(a)[0].colSpan=Z(d);e.push(c[0])}};if(h.isArray(a)||
|
||||
a instanceof h)for(var g=0,j=a.length;g<j;g++)f(a[g],b);else f(a,b);c._details&&c._details.remove();c._details=h(e);c._detailsShow&&c._details.insertAfter(c.nTr)}return this});r(["row().child.show()","row().child().show()"],function(){Tb(this,!0);return this});r(["row().child.hide()","row().child().hide()"],function(){Tb(this,!1);return this});r(["row().child.remove()","row().child().remove()"],function(){bb(this);return this});r("row().child.isShown()",function(){var a=this.context;return a.length&&
|
||||
this.length?a[0].aoData[this[0]]._detailsShow||!1:!1});var ac=/^(.*):(name|visIdx|visible)$/;r("columns()",function(a,b){a===l?a="":h.isPlainObject(a)&&(b=a,a="");var b=$a(b),c=this.iterator("table",function(b){var c=a,f=b.aoColumns,g=C(f,"sName"),j=C(f,"nTh");return Za(c,function(a){var c=Ob(a);if(a==="")return S(f.length);if(c!==null)return[c>=0?c:f.length+c];var e=typeof a==="string"?a.match(ac):"";if(e)switch(e[2]){case "visIdx":case "visible":a=parseInt(e[1],10);if(a<0){c=h.map(f,function(a,
|
||||
b){return a.bVisible?b:null});return[c[c.length+a]]}return[ga(b,a)];case "name":return h.map(g,function(a,b){return a===e[1]?b:null})}else return h(j).filter(a).map(function(){return h.inArray(this,j)}).toArray()})});c.selector.cols=a;c.selector.opts=b;return c});v("columns().header()","column().header()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTh})});v("columns().footer()","column().footer()",function(){return this.iterator("column",function(a,b){return a.aoColumns[b].nTf})});
|
||||
v("columns().data()","column().data()",function(){return this.iterator("column-rows",function(a,b,c,d,e){for(var c=[],d=0,f=e.length;d<f;d++)c.push(A(a,e[d],b,""));return c})});v("columns().cache()","column().cache()",function(a){return this.iterator("column-rows",function(b,c,d,e,f){return xa(b.aoData,f,"search"===a?"_aFilterData":"_aSortData",c)})});v("columns().nodes()","column().nodes()",function(){return this.iterator("column-rows",function(a,b,c,d,e){return xa(a.aoData,e,"anCells",b)})});v("columns().visible()",
|
||||
"column().visible()",function(a){return this.iterator("column",function(b,c){var d;if(a===l)d=b.aoColumns[c].bVisible;else{var e=b.aoColumns;d=e[c];var f=b.aoData,g,j,i;if(a===l)d=d.bVisible;else{if(d.bVisible!==a){if(a){var o=h.inArray(!0,C(e,"bVisible"),c+1);g=0;for(j=f.length;g<j;g++)i=f[g].nTr,e=f[g].anCells,i&&i.insertBefore(e[c],e[o]||null)}else h(C(b.aoData,"anCells",c)).detach();d.bVisible=a;ba(b,b.aoHeader);ba(b,b.aoFooter);V(b);(b.oScroll.sX||b.oScroll.sY)&&W(b);u(b,null,"column-visibility",
|
||||
[b,c,a]);ta(b)}d=void 0}}return d})});v("columns().indexes()","column().index()",function(a){return this.iterator("column",function(b,c){return"visible"===a?Y(b,c):c})});r("columns.adjust()",function(){return this.iterator("table",function(a){V(a)})});r("column.index()",function(a,b){if(0!==this.context.length){var c=this.context[0];if("fromVisible"===a||"toData"===a)return ga(c,b);if("fromData"===a||"toVisible"===a)return Y(c,b)}});r("column()",function(a,b){return ab(this.columns(a,b))});r("cells()",
|
||||
function(a,b,c){h.isPlainObject(a)&&(typeof a.row!==l?(c=b,b=null):(c=a,a=null));h.isPlainObject(b)&&(c=b,b=null);if(null===b||b===l)return this.iterator("table",function(b){var d=a,e=$a(c),f=b.aoData,g=Ya(b,e),e=xa(f,g,"anCells"),i=h([].concat.apply([],e)),j,m=b.aoColumns.length,o,p,r,q;return Za(d,function(a){if(a===null||a===l){o=[];p=0;for(r=g.length;p<r;p++){j=g[p];for(q=0;q<m;q++)o.push({row:j,column:q})}return o}return h.isPlainObject(a)?[a]:i.filter(a).map(function(a,b){j=b.parentNode._DT_RowIndex;
|
||||
return{row:j,column:h.inArray(b,f[j].anCells)}}).toArray()})});var d=this.columns(b,c),e=this.rows(a,c),f,g,j,i,o,m=this.iterator("table",function(a,b){f=[];g=0;for(j=e[b].length;g<j;g++){i=0;for(o=d[b].length;i<o;i++)f.push({row:e[b][g],column:d[b][i]})}return f});h.extend(m.selector,{cols:b,rows:a,opts:c});return m});v("cells().nodes()","cell().node()",function(){return this.iterator("cell",function(a,b,c){return a.aoData[b].anCells[c]})});r("cells().data()",function(){return this.iterator("cell",
|
||||
function(a,b,c){return A(a,b,c)})});v("cells().cache()","cell().cache()",function(a){a="search"===a?"_aFilterData":"_aSortData";return this.iterator("cell",function(b,c,d){return b.aoData[c][a][d]})});v("cells().indexes()","cell().index()",function(){return this.iterator("cell",function(a,b,c){return{row:b,column:c,columnVisible:Y(a,c)}})});r(["cells().invalidate()","cell().invalidate()"],function(a){var b=this.selector;this.rows(b.rows,b.opts).invalidate(a);return this});r("cell()",function(a,b,
|
||||
c){return ab(this.cells(a,b,c))});r("cell().data()",function(a){var b=this.context,c=this[0];if(a===l)return b.length&&c.length?A(b[0],c[0].row,c[0].column):l;Ea(b[0],c[0].row,c[0].column,a);la(b[0],c[0].row,"data",c[0].column);return this});r("order()",function(a,b){var c=this.context;if(a===l)return 0!==c.length?c[0].aaSorting:l;"number"===typeof a?a=[[a,b]]:h.isArray(a[0])||(a=Array.prototype.slice.call(arguments));return this.iterator("table",function(b){b.aaSorting=a.slice()})});r("order.listener()",
|
||||
function(a,b,c){return this.iterator("table",function(d){Ka(d,a,b,c)})});r(["columns().order()","column().order()"],function(a){var b=this;return this.iterator("table",function(c,d){var e=[];h.each(b[d],function(b,c){e.push([c,a])});c.aaSorting=e})});r("search()",function(a,b,c,d){var e=this.context;return a===l?0!==e.length?e[0].oPreviousSearch.sSearch:l:this.iterator("table",function(e){e.oFeatures.bFilter&&ca(e,h.extend({},e.oPreviousSearch,{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:
|
||||
c,bCaseInsensitive:null===d?!0:d}),1)})});r(["columns().search()","column().search()"],function(a,b,c,d){return this.iterator("column",function(e,f){var g=e.aoPreSearchCols;if(a===l)return g[f].sSearch;e.oFeatures.bFilter&&(h.extend(g[f],{sSearch:a+"",bRegex:null===b?!1:b,bSmart:null===c?!0:c,bCaseInsensitive:null===d?!0:d}),ca(e,e.oPreviousSearch,1))})});r("state()",function(){return this.context.length?this.context[0].oSavedState:null});r("state.clear()",function(){return this.iterator("table",
|
||||
function(a){a.fnStateSaveCallback.call(a.oInstance,a,{})})});r("state.loaded()",function(){return this.context.length?this.context[0].oLoadedState:null});r("state.save()",function(){return this.iterator("table",function(a){ta(a)})});p.versionCheck=p.fnVersionCheck=function(a){for(var b=p.version.split("."),a=a.split("."),c,d,e=0,f=a.length;e<f;e++)if(c=parseInt(b[e],10)||0,d=parseInt(a[e],10)||0,c!==d)return c>d;return!0};p.isDataTable=p.fnIsDataTable=function(a){var b=h(a).get(0),c=!1;h.each(p.settings,
|
||||
function(a,e){if(e.nTable===b||e.nScrollHead===b||e.nScrollFoot===b)c=!0});return c};p.tables=p.fnTables=function(a){return jQuery.map(p.settings,function(b){if(!a||a&&h(b.nTable).is(":visible"))return b.nTable})};p.camelToHungarian=G;r("$()",function(a,b){var c=this.rows(b).nodes(),c=h(c);return h([].concat(c.filter(a).toArray(),c.find(a).toArray()))});h.each(["on","one","off"],function(a,b){r(b+"()",function(){var a=Array.prototype.slice.call(arguments);a[0].match(/\.dt\b/)||(a[0]+=".dt");var d=
|
||||
h(this.tables().nodes());d[b].apply(d,a);return this})});r("clear()",function(){return this.iterator("table",function(a){ja(a)})});r("settings()",function(){return new q(this.context,this.context)});r("data()",function(){return this.iterator("table",function(a){return C(a.aoData,"_aData")}).flatten()});r("destroy()",function(a){a=a||!1;return this.iterator("table",function(b){var c=b.nTableWrapper.parentNode,d=b.oClasses,e=b.nTable,f=b.nTBody,g=b.nTHead,j=b.nTFoot,i=h(e),f=h(f),l=h(b.nTableWrapper),
|
||||
m=h.map(b.aoData,function(a){return a.nTr}),n;b.bDestroying=!0;u(b,"aoDestroyCallback","destroy",[b]);a||(new q(b)).columns().visible(!0);l.unbind(".DT").find(":not(tbody *)").unbind(".DT");h(za).unbind(".DT-"+b.sInstance);e!=g.parentNode&&(i.children("thead").detach(),i.append(g));j&&e!=j.parentNode&&(i.children("tfoot").detach(),i.append(j));i.detach();l.detach();b.aaSorting=[];b.aaSortingFixed=[];sa(b);h(m).removeClass(b.asStripeClasses.join(" "));h("th, td",g).removeClass(d.sSortable+" "+d.sSortableAsc+
|
||||
" "+d.sSortableDesc+" "+d.sSortableNone);b.bJUI&&(h("th span."+d.sSortIcon+", td span."+d.sSortIcon,g).detach(),h("th, td",g).each(function(){var a=h("div."+d.sSortJUIWrapper,this);h(this).append(a.contents());a.detach()}));!a&&c&&c.insertBefore(e,b.nTableReinsertBefore);f.children().detach();f.append(m);i.css("width",b.sDestroyWidth).removeClass(d.sTable);(n=b.asDestroyStripes.length)&&f.children().each(function(a){h(this).addClass(b.asDestroyStripes[a%n])});c=h.inArray(b,p.settings);-1!==c&&p.settings.splice(c,
|
||||
1)})});p.version="1.10.1";p.settings=[];p.models={};p.models.oSearch={bCaseInsensitive:!0,sSearch:"",bRegex:!1,bSmart:!0};p.models.oRow={nTr:null,anCells:null,_aData:[],_aSortData:null,_aFilterData:null,_sFilterRow:null,_sRowStripe:"",src:null};p.models.oColumn={idx:null,aDataSort:null,asSorting:null,bSearchable:null,bSortable:null,bVisible:null,_sManualType:null,_bAttrSrc:!1,fnCreatedCell:null,fnGetData:null,fnSetData:null,mData:null,mRender:null,nTh:null,nTf:null,sClass:null,sContentPadding:null,
|
||||
sDefaultContent:null,sName:null,sSortDataType:"std",sSortingClass:null,sSortingClassJUI:null,sTitle:null,sType:null,sWidth:null,sWidthOrig:null};p.defaults={aaData:null,aaSorting:[[0,"asc"]],aaSortingFixed:[],ajax:null,aLengthMenu:[10,25,50,100],aoColumns:null,aoColumnDefs:null,aoSearchCols:[],asStripeClasses:null,bAutoWidth:!0,bDeferRender:!1,bDestroy:!1,bFilter:!0,bInfo:!0,bJQueryUI:!1,bLengthChange:!0,bPaginate:!0,bProcessing:!1,bRetrieve:!1,bScrollCollapse:!1,bServerSide:!1,bSort:!0,bSortMulti:!0,
|
||||
bSortCellsTop:!1,bSortClasses:!0,bStateSave:!1,fnCreatedRow:null,fnDrawCallback:null,fnFooterCallback:null,fnFormatNumber:function(a){return a.toString().replace(/\B(?=(\d{3})+(?!\d))/g,this.oLanguage.sThousands)},fnHeaderCallback:null,fnInfoCallback:null,fnInitComplete:null,fnPreDrawCallback:null,fnRowCallback:null,fnServerData:null,fnServerParams:null,fnStateLoadCallback:function(a){try{return JSON.parse((-1===a.iStateDuration?sessionStorage:localStorage).getItem("DataTables_"+a.sInstance+"_"+location.pathname))}catch(b){}},
|
||||
fnStateLoadParams:null,fnStateLoaded:null,fnStateSaveCallback:function(a,b){try{(-1===a.iStateDuration?sessionStorage:localStorage).setItem("DataTables_"+a.sInstance+"_"+location.pathname,JSON.stringify(b))}catch(c){}},fnStateSaveParams:null,iStateDuration:7200,iDeferLoading:null,iDisplayLength:10,iDisplayStart:0,iTabIndex:0,oClasses:{},oLanguage:{oAria:{sSortAscending:": activate to sort column ascending",sSortDescending:": activate to sort column descending"},oPaginate:{sFirst:"First",sLast:"Last",
|
||||
sNext:"Next",sPrevious:"Previous"},sEmptyTable:"No data available in table",sInfo:"Showing _START_ to _END_ of _TOTAL_ entries",sInfoEmpty:"Showing 0 to 0 of 0 entries",sInfoFiltered:"(filtered from _MAX_ total entries)",sInfoPostFix:"",sDecimal:"",sThousands:",",sLengthMenu:"Show _MENU_ entries",sLoadingRecords:"Loading...",sProcessing:"Processing...",sSearch:"Search:",sSearchPlaceholder:"",sUrl:"",sZeroRecords:"No matching records found"},oSearch:h.extend({},p.models.oSearch),sAjaxDataProp:"data",
|
||||
sAjaxSource:null,sDom:"lfrtip",sPaginationType:"simple_numbers",sScrollX:"",sScrollXInner:"",sScrollY:"",sServerMethod:"GET",renderer:null};T(p.defaults);p.defaults.column={aDataSort:null,iDataSort:-1,asSorting:["asc","desc"],bSearchable:!0,bSortable:!0,bVisible:!0,fnCreatedCell:null,mData:null,mRender:null,sCellType:"td",sClass:"",sContentPadding:"",sDefaultContent:null,sName:"",sSortDataType:"std",sTitle:null,sType:null,sWidth:null};T(p.defaults.column);p.models.oSettings={oFeatures:{bAutoWidth:null,
|
||||
bDeferRender:null,bFilter:null,bInfo:null,bLengthChange:null,bPaginate:null,bProcessing:null,bServerSide:null,bSort:null,bSortMulti:null,bSortClasses:null,bStateSave:null},oScroll:{bCollapse:null,iBarWidth:0,sX:null,sXInner:null,sY:null},oLanguage:{fnInfoCallback:null},oBrowser:{bScrollOversize:!1,bScrollbarLeft:!1},ajax:null,aanFeatures:[],aoData:[],aiDisplay:[],aiDisplayMaster:[],aoColumns:[],aoHeader:[],aoFooter:[],oPreviousSearch:{},aoPreSearchCols:[],aaSorting:null,aaSortingFixed:[],asStripeClasses:null,
|
||||
asDestroyStripes:[],sDestroyWidth:0,aoRowCallback:[],aoHeaderCallback:[],aoFooterCallback:[],aoDrawCallback:[],aoRowCreatedCallback:[],aoPreDrawCallback:[],aoInitComplete:[],aoStateSaveParams:[],aoStateLoadParams:[],aoStateLoaded:[],sTableId:"",nTable:null,nTHead:null,nTFoot:null,nTBody:null,nTableWrapper:null,bDeferLoading:!1,bInitialised:!1,aoOpenRows:[],sDom:null,sPaginationType:"two_button",iStateDuration:0,aoStateSave:[],aoStateLoad:[],oSavedState:null,oLoadedState:null,sAjaxSource:null,sAjaxDataProp:null,
|
||||
bAjaxDataGet:!0,jqXHR:null,json:l,oAjaxData:l,fnServerData:null,aoServerParams:[],sServerMethod:null,fnFormatNumber:null,aLengthMenu:null,iDraw:0,bDrawing:!1,iDrawError:-1,_iDisplayLength:10,_iDisplayStart:0,_iRecordsTotal:0,_iRecordsDisplay:0,bJUI:null,oClasses:{},bFiltered:!1,bSorted:!1,bSortCellsTop:null,oInit:null,aoDestroyCallback:[],fnRecordsTotal:function(){return"ssp"==z(this)?1*this._iRecordsTotal:this.aiDisplayMaster.length},fnRecordsDisplay:function(){return"ssp"==z(this)?1*this._iRecordsDisplay:
|
||||
this.aiDisplay.length},fnDisplayEnd:function(){var a=this._iDisplayLength,b=this._iDisplayStart,c=b+a,d=this.aiDisplay.length,e=this.oFeatures,f=e.bPaginate;return e.bServerSide?!1===f||-1===a?b+d:Math.min(b+a,this._iRecordsDisplay):!f||c>d||-1===a?d:c},oInstance:null,sInstance:null,iTabIndex:0,nScrollHead:null,nScrollFoot:null,aLastSort:[],oPlugins:{}};p.ext=t={classes:{},errMode:"alert",feature:[],search:[],internal:{},legacy:{ajax:null},pager:{},renderer:{pageButton:{},header:{}},order:{},type:{detect:[],
|
||||
search:{},order:{}},_unique:0,fnVersionCheck:p.fnVersionCheck,iApiIndex:0,oJUIClasses:{},sVersion:p.version};h.extend(t,{afnFiltering:t.search,aTypes:t.type.detect,ofnSearch:t.type.search,oSort:t.type.order,afnSortData:t.order,aoFeatures:t.feature,oApi:t.internal,oStdClasses:t.classes,oPagination:t.pager});h.extend(p.ext.classes,{sTable:"dataTable",sNoFooter:"no-footer",sPageButton:"paginate_button",sPageButtonActive:"current",sPageButtonDisabled:"disabled",sStripeOdd:"odd",sStripeEven:"even",sRowEmpty:"dataTables_empty",
|
||||
sWrapper:"dataTables_wrapper",sFilter:"dataTables_filter",sInfo:"dataTables_info",sPaging:"dataTables_paginate paging_",sLength:"dataTables_length",sProcessing:"dataTables_processing",sSortAsc:"sorting_asc",sSortDesc:"sorting_desc",sSortable:"sorting",sSortableAsc:"sorting_asc_disabled",sSortableDesc:"sorting_desc_disabled",sSortableNone:"sorting_disabled",sSortColumn:"sorting_",sFilterInput:"",sLengthSelect:"",sScrollWrapper:"dataTables_scroll",sScrollHead:"dataTables_scrollHead",sScrollHeadInner:"dataTables_scrollHeadInner",
|
||||
sScrollBody:"dataTables_scrollBody",sScrollFoot:"dataTables_scrollFoot",sScrollFootInner:"dataTables_scrollFootInner",sHeaderTH:"",sFooterTH:"",sSortJUIAsc:"",sSortJUIDesc:"",sSortJUI:"",sSortJUIAscAllowed:"",sSortJUIDescAllowed:"",sSortJUIWrapper:"",sSortIcon:"",sJUIHeader:"",sJUIFooter:""});var ya="",ya="",E=ya+"ui-state-default",ea=ya+"css_right ui-icon ui-icon-",Ub=ya+"fg-toolbar ui-toolbar ui-widget-header ui-helper-clearfix";h.extend(p.ext.oJUIClasses,p.ext.classes,{sPageButton:"fg-button ui-button "+
|
||||
E,sPageButtonActive:"ui-state-disabled",sPageButtonDisabled:"ui-state-disabled",sPaging:"dataTables_paginate fg-buttonset ui-buttonset fg-buttonset-multi ui-buttonset-multi paging_",sSortAsc:E+" sorting_asc",sSortDesc:E+" sorting_desc",sSortable:E+" sorting",sSortableAsc:E+" sorting_asc_disabled",sSortableDesc:E+" sorting_desc_disabled",sSortableNone:E+" sorting_disabled",sSortJUIAsc:ea+"triangle-1-n",sSortJUIDesc:ea+"triangle-1-s",sSortJUI:ea+"carat-2-n-s",sSortJUIAscAllowed:ea+"carat-1-n",sSortJUIDescAllowed:ea+
|
||||
"carat-1-s",sSortJUIWrapper:"DataTables_sort_wrapper",sSortIcon:"DataTables_sort_icon",sScrollHead:"dataTables_scrollHead "+E,sScrollFoot:"dataTables_scrollFoot "+E,sHeaderTH:E,sFooterTH:E,sJUIHeader:Ub+" ui-corner-tl ui-corner-tr",sJUIFooter:Ub+" ui-corner-bl ui-corner-br"});var Lb=p.ext.pager;h.extend(Lb,{simple:function(){return["previous","next"]},full:function(){return["first","previous","next","last"]},simple_numbers:function(a,b){return["previous",Ua(a,b),"next"]},full_numbers:function(a,b){return["first",
|
||||
"previous",Ua(a,b),"next","last"]},_numbers:Ua,numbers_length:7});h.extend(!0,p.ext.renderer,{pageButton:{_:function(a,b,c,d,e,f){var g=a.oClasses,j=a.oLanguage.oPaginate,i,l,m=0,n=function(b,d){var k,p,r,q,s=function(b){Ra(a,b.data.action,true)};k=0;for(p=d.length;k<p;k++){q=d[k];if(h.isArray(q)){r=h("<"+(q.DT_el||"div")+"/>").appendTo(b);n(r,q)}else{l=i="";switch(q){case "ellipsis":b.append("<span>…</span>");break;case "first":i=j.sFirst;l=q+(e>0?"":" "+g.sPageButtonDisabled);break;case "previous":i=
|
||||
j.sPrevious;l=q+(e>0?"":" "+g.sPageButtonDisabled);break;case "next":i=j.sNext;l=q+(e<f-1?"":" "+g.sPageButtonDisabled);break;case "last":i=j.sLast;l=q+(e<f-1?"":" "+g.sPageButtonDisabled);break;default:i=q+1;l=e===q?g.sPageButtonActive:""}if(i){r=h("<a>",{"class":g.sPageButton+" "+l,"aria-controls":a.sTableId,"data-dt-idx":m,tabindex:a.iTabIndex,id:c===0&&typeof q==="string"?a.sTableId+"_"+q:null}).html(i).appendTo(b);Ta(r,{action:q},s);m++}}}};try{var k=h(O.activeElement).data("dt-idx");n(h(b).empty(),
|
||||
d);k!==null&&h(b).find("[data-dt-idx="+k+"]").focus()}catch(p){}}}});var va=function(a,b,c,d){if(!a||"-"===a)return-Infinity;b&&(a=Pb(a,b));a.replace&&(c&&(a=a.replace(c,"")),d&&(a=a.replace(d,"")));return 1*a};h.extend(t.type.order,{"date-pre":function(a){return Date.parse(a)||0},"html-pre":function(a){return H(a)?"":a.replace?a.replace(/<.*?>/g,"").toLowerCase():a+""},"string-pre":function(a){return H(a)?"":"string"===typeof a?a.toLowerCase():!a.toString?"":a.toString()},"string-asc":function(a,
|
||||
b){return a<b?-1:a>b?1:0},"string-desc":function(a,b){return a<b?1:a>b?-1:0}});cb("");h.extend(p.ext.type.detect,[function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c)?"num"+c:null},function(a){if(a&&(!Yb.test(a)||!Zb.test(a)))return null;var b=Date.parse(a);return null!==b&&!isNaN(b)||H(a)?"date":null},function(a,b){var c=b.oLanguage.sDecimal;return Xa(a,c,!0)?"num-fmt"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Qb(a,c)?"html-num"+c:null},function(a,b){var c=b.oLanguage.sDecimal;return Qb(a,
|
||||
c,!0)?"html-num-fmt"+c:null},function(a){return H(a)||"string"===typeof a&&-1!==a.indexOf("<")?"html":null}]);h.extend(p.ext.type.search,{html:function(a){return H(a)?a:"string"===typeof a?a.replace(Nb," ").replace(wa,""):""},string:function(a){return H(a)?a:"string"===typeof a?a.replace(Nb," "):a}});h.extend(!0,p.ext.renderer,{header:{_:function(a,b,c,d){h(a.nTable).on("order.dt.DT",function(e,f,g,h){if(a===f){e=c.idx;b.removeClass(c.sSortingClass+" "+d.sSortAsc+" "+d.sSortDesc).addClass(h[e]=="asc"?
|
||||
d.sSortAsc:h[e]=="desc"?d.sSortDesc:c.sSortingClass)}})},jqueryui:function(a,b,c,d){var e=c.idx;h("<div/>").addClass(d.sSortJUIWrapper).append(b.contents()).append(h("<span/>").addClass(d.sSortIcon+" "+c.sSortingClassJUI)).appendTo(b);h(a.nTable).on("order.dt.DT",function(f,g,h,i){if(a===g){b.removeClass(d.sSortAsc+" "+d.sSortDesc).addClass(i[e]=="asc"?d.sSortAsc:i[e]=="desc"?d.sSortDesc:c.sSortingClass);b.find("span."+d.sSortIcon).removeClass(d.sSortJUIAsc+" "+d.sSortJUIDesc+" "+d.sSortJUI+" "+d.sSortJUIAscAllowed+
|
||||
" "+d.sSortJUIDescAllowed).addClass(i[e]=="asc"?d.sSortJUIAsc:i[e]=="desc"?d.sSortJUIDesc:c.sSortingClassJUI)}})}}});p.render={number:function(a,b,c,d){return{display:function(e){var f=0>e?"-":"",e=Math.abs(parseFloat(e)),g=parseInt(e,10),e=c?b+(e-g).toFixed(c).substring(2):"";return f+(d||"")+g.toString().replace(/\B(?=(\d{3})+(?!\d))/g,a)+e}}}};h.extend(p.ext.internal,{_fnExternApiFunc:Mb,_fnBuildAjax:na,_fnAjaxUpdate:jb,_fnAjaxParameters:sb,_fnAjaxUpdateDraw:tb,_fnAjaxDataSrc:oa,_fnAddColumn:Aa,
|
||||
_fnColumnOptions:fa,_fnAdjustColumnSizing:V,_fnVisibleToColumnIndex:ga,_fnColumnIndexToVisible:Y,_fnVisbleColumns:Z,_fnGetColumns:X,_fnColumnTypes:Da,_fnApplyColumnDefs:hb,_fnHungarianMap:T,_fnCamelToHungarian:G,_fnLanguageCompat:N,_fnBrowserDetect:fb,_fnAddData:I,_fnAddTr:ha,_fnNodeToDataIndex:function(a,b){return b._DT_RowIndex!==l?b._DT_RowIndex:null},_fnNodeToColumnIndex:function(a,b,c){return h.inArray(c,a.aoData[b].anCells)},_fnGetCellData:A,_fnSetCellData:Ea,_fnSplitObjNotation:Ga,_fnGetObjectDataFn:U,
|
||||
_fnSetObjectDataFn:Ba,_fnGetDataMaster:Ha,_fnClearTable:ja,_fnDeleteIndex:ka,_fnInvalidateRow:la,_fnGetRowElements:ia,_fnCreateTr:Fa,_fnBuildHead:ib,_fnDrawHead:ba,_fnDraw:K,_fnReDraw:L,_fnAddOptionsHtml:lb,_fnDetectHeader:aa,_fnGetUniqueThs:ma,_fnFeatureHtmlFilter:nb,_fnFilterComplete:ca,_fnFilterCustom:wb,_fnFilterColumn:vb,_fnFilter:ub,_fnFilterCreateSearch:Na,_fnEscapeRegex:Oa,_fnFilterData:xb,_fnFeatureHtmlInfo:qb,_fnUpdateInfo:Ab,_fnInfoMacros:Bb,_fnInitialise:ra,_fnInitComplete:pa,_fnLengthChange:Pa,
|
||||
_fnFeatureHtmlLength:mb,_fnFeatureHtmlPaginate:rb,_fnPageChange:Ra,_fnFeatureHtmlProcessing:ob,_fnProcessingDisplay:B,_fnFeatureHtmlTable:pb,_fnScrollDraw:W,_fnApplyToChildren:F,_fnCalculateColumnWidths:Ca,_fnThrottle:Ma,_fnConvertToWidth:Cb,_fnScrollingWidthAdjust:Eb,_fnGetWidestNode:Db,_fnGetMaxLenString:Fb,_fnStringToCss:s,_fnScrollBarWidth:Gb,_fnSortFlatten:R,_fnSort:kb,_fnSortAria:Ib,_fnSortListener:Sa,_fnSortAttachListener:Ka,_fnSortingClasses:sa,_fnSortData:Hb,_fnSaveState:ta,_fnLoadState:Jb,
|
||||
_fnSettingsFromNode:ua,_fnLog:P,_fnMap:D,_fnBindAction:Ta,_fnCallbackReg:x,_fnCallbackFire:u,_fnLengthOverflow:Qa,_fnRenderer:La,_fnDataSource:z,_fnRowAttributes:Ia,_fnCalculateEnd:function(){}});h.fn.dataTable=p;h.fn.dataTableSettings=p.settings;h.fn.dataTableExt=p.ext;h.fn.DataTable=function(a){return h(this).dataTable(a).api()};h.each(p,function(a,b){h.fn.DataTable[a]=b});return h.fn.dataTable};"function"===typeof define&&define.amd?define("datatables",["jquery"],N):"object"===typeof exports?N(require("jquery")):
|
||||
jQuery&&!jQuery.fn.dataTable&&N(jQuery)})(window,document);
|
23
bigbluebutton-client/resources/prod/lib/jquery.json-2.4.min.js
vendored
Normal file
23
bigbluebutton-client/resources/prod/lib/jquery.json-2.4.min.js
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/*! jQuery JSON plugin 2.4.0 | code.google.com/p/jquery-json */
|
||||
(function($){'use strict';var escape=/["\\\x00-\x1f\x7f-\x9f]/g,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},hasOwn=Object.prototype.hasOwnProperty;$.toJSON=typeof JSON==='object'&&JSON.stringify?JSON.stringify:function(o){if(o===null){return'null';}
|
||||
var pairs,k,name,val,type=$.type(o);if(type==='undefined'){return undefined;}
|
||||
if(type==='number'||type==='boolean'){return String(o);}
|
||||
if(type==='string'){return $.quoteString(o);}
|
||||
if(typeof o.toJSON==='function'){return $.toJSON(o.toJSON());}
|
||||
if(type==='date'){var month=o.getUTCMonth()+1,day=o.getUTCDate(),year=o.getUTCFullYear(),hours=o.getUTCHours(),minutes=o.getUTCMinutes(),seconds=o.getUTCSeconds(),milli=o.getUTCMilliseconds();if(month<10){month='0'+month;}
|
||||
if(day<10){day='0'+day;}
|
||||
if(hours<10){hours='0'+hours;}
|
||||
if(minutes<10){minutes='0'+minutes;}
|
||||
if(seconds<10){seconds='0'+seconds;}
|
||||
if(milli<100){milli='0'+milli;}
|
||||
if(milli<10){milli='0'+milli;}
|
||||
return'"'+year+'-'+month+'-'+day+'T'+
|
||||
hours+':'+minutes+':'+seconds+'.'+milli+'Z"';}
|
||||
pairs=[];if($.isArray(o)){for(k=0;k<o.length;k++){pairs.push($.toJSON(o[k])||'null');}
|
||||
return'['+pairs.join(',')+']';}
|
||||
if(typeof o==='object'){for(k in o){if(hasOwn.call(o,k)){type=typeof k;if(type==='number'){name='"'+k+'"';}else if(type==='string'){name=$.quoteString(k);}else{continue;}
|
||||
type=typeof o[k];if(type!=='function'&&type!=='undefined'){val=$.toJSON(o[k]);pairs.push(name+':'+val);}}}
|
||||
return'{'+pairs.join(',')+'}';}};$.evalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){return eval('('+str+')');};$.secureEvalJSON=typeof JSON==='object'&&JSON.parse?JSON.parse:function(str){var filtered=str.replace(/\\["\\\/bfnrtu]/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,'');if(/^[\],:{}\s]*$/.test(filtered)){return eval('('+str+')');}
|
||||
throw new SyntaxError('Error parsing JSON, source is not valid.');};$.quoteString=function(str){if(str.match(escape)){return'"'+str.replace(escape,function(a){var c=meta[a];if(typeof c==='string'){return c;}
|
||||
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+(c%16).toString(16);})+'"';}
|
||||
return'"'+str+'"';};}(jQuery));
|
716
bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js
Executable file
716
bigbluebutton-client/resources/prod/lib/jquery.jsonrpcclient.js
Executable file
@ -0,0 +1,716 @@
|
||||
/*
|
||||
* Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH
|
||||
* Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* Version: MPL 1.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is jquery.jsonrpclient.js modified for Verto HTML5/Javascript Telephony Signaling and Control Protocol Stack for FreeSWITCH
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Textalk AB http://textalk.se/
|
||||
* Portions created by the Initial Developer are Copyright (C)
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
*
|
||||
* Anthony Minessale II <anthm@freeswitch.org>
|
||||
*
|
||||
* jquery.jsonrpclient.js - JSON RPC client code
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* This plugin requires jquery.json.js to be available, or at least the methods $.toJSON and
|
||||
* $.parseJSON.
|
||||
*
|
||||
* The plan is to make use of websockets if they are available, but work just as well with only
|
||||
* http if not.
|
||||
*
|
||||
* Usage example:
|
||||
*
|
||||
* var foo = new $.JsonRpcClient({ ajaxUrl: '/backend/jsonrpc' });
|
||||
* foo.call(
|
||||
* 'bar', [ 'A parameter', 'B parameter' ],
|
||||
* function(result) { alert('Foo bar answered: ' + result.my_answer); },
|
||||
* function(error) { console.log('There was an error', error); }
|
||||
* );
|
||||
*
|
||||
* More examples are available in README.md
|
||||
*/
|
||||
(function($) {
|
||||
/**
|
||||
* @fn new
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param options An object stating the backends:
|
||||
* ajaxUrl A url (relative or absolute) to a http(s) backend.
|
||||
* socketUrl A url (relative of absolute) to a ws(s) backend.
|
||||
* onmessage A socket message handler for other messages (non-responses).
|
||||
* getSocket A function returning a WebSocket or null.
|
||||
* It must take an onmessage_cb and bind it to the onmessage event
|
||||
* (or chain it before/after some other onmessage handler).
|
||||
* Or, it could return null if no socket is available.
|
||||
* The returned instance must have readyState <= 1, and if less than 1,
|
||||
* react to onopen binding.
|
||||
*/
|
||||
$.JsonRpcClient = function(options) {
|
||||
var self = this;
|
||||
this.options = $.extend({
|
||||
ajaxUrl : null,
|
||||
socketUrl : null, ///< The ws-url for default getSocket.
|
||||
onmessage : null, ///< Other onmessage-handler.
|
||||
login : null, /// auth login
|
||||
passwd : null, /// auth passwd
|
||||
sessid : null,
|
||||
loginParams : null,
|
||||
userVariables : null,
|
||||
getSocket : function(onmessage_cb) { return self._getSocket(onmessage_cb); }
|
||||
}, options);
|
||||
|
||||
self.ws_cnt = 0;
|
||||
|
||||
// Declare an instance version of the onmessage callback to wrap 'this'.
|
||||
this.wsOnMessage = function(event) { self._wsOnMessage(event); };
|
||||
};
|
||||
|
||||
/// Holding the WebSocket on default getsocket.
|
||||
$.JsonRpcClient.prototype._ws_socket = null;
|
||||
|
||||
/// Object <id>: { success_cb: cb, error_cb: cb }
|
||||
$.JsonRpcClient.prototype._ws_callbacks = {};
|
||||
|
||||
/// The next JSON-RPC request id.
|
||||
$.JsonRpcClient.prototype._current_id = 1;
|
||||
|
||||
|
||||
$.JsonRpcClient.prototype.speedTest = function (bytes, cb) {
|
||||
var socket = this.options.getSocket(this.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
this.speedCB = cb;
|
||||
this.speedBytes = bytes;
|
||||
socket.send("#SPU " + bytes);
|
||||
|
||||
var loops = bytes / 1024;
|
||||
var rem = bytes % 1024;
|
||||
var i;
|
||||
var data = new Array(1024).join(".");
|
||||
for (i = 0; i < loops; i++) {
|
||||
socket.send("#SPB " + data);
|
||||
}
|
||||
|
||||
if (rem) {
|
||||
socket.send("#SPB " + data);
|
||||
}
|
||||
|
||||
socket.send("#SPE");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @fn call
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param method The method to run on JSON-RPC server.
|
||||
* @param params The params; an array or object.
|
||||
* @param success_cb A callback for successful request.
|
||||
* @param error_cb A callback for error.
|
||||
*/
|
||||
$.JsonRpcClient.prototype.call = function(method, params, success_cb, error_cb) {
|
||||
// Construct the JSON-RPC 2.0 request.
|
||||
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
var request = {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params,
|
||||
id : this._current_id++ // Increase the id counter to match request/response
|
||||
};
|
||||
|
||||
if (!success_cb) {
|
||||
success_cb = function(e){console.log("Success: ", e);};
|
||||
}
|
||||
|
||||
if (!error_cb) {
|
||||
error_cb = function(e){console.log("Error: ", e);};
|
||||
}
|
||||
|
||||
// Try making a WebSocket call.
|
||||
var socket = this.options.getSocket(this.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
this._wsCall(socket, request, success_cb, error_cb);
|
||||
return;
|
||||
}
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (this.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.call used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : this.options.ajaxUrl,
|
||||
data : $.toJSON(request),
|
||||
dataType : 'json',
|
||||
cache : false,
|
||||
|
||||
success : function(data) {
|
||||
if ('error' in data) error_cb(data.error, this);
|
||||
success_cb(data.result, this);
|
||||
},
|
||||
|
||||
// JSON-RPC Server could return non-200 on error
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
try {
|
||||
var response = $.parseJSON(jqXHR.responseText);
|
||||
|
||||
if ('console' in window) console.log(response);
|
||||
|
||||
error_cb(response.error, this);
|
||||
} catch (err) {
|
||||
// Perhaps the responseText wasn't really a jsonrpc-error.
|
||||
error_cb({ error: jqXHR.responseText }, this);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify sends a command to the server that won't need a response. In http, there is probably
|
||||
* an empty response - that will be dropped, but in ws there should be no response at all.
|
||||
*
|
||||
* This is very similar to call, but has no id and no handling of callbacks.
|
||||
*
|
||||
* @fn notify
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param method The method to run on JSON-RPC server.
|
||||
* @param params The params; an array or object.
|
||||
*/
|
||||
$.JsonRpcClient.prototype.notify = function(method, params) {
|
||||
// Construct the JSON-RPC 2.0 request.
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
var request = {
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params
|
||||
};
|
||||
|
||||
// Try making a WebSocket call.
|
||||
var socket = this.options.getSocket(this.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
this._wsCall(socket, request);
|
||||
return;
|
||||
}
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (this.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.notify used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type : 'POST',
|
||||
url : this.options.ajaxUrl,
|
||||
data : $.toJSON(request),
|
||||
dataType : 'json',
|
||||
cache : false
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Make a batch-call by using a callback.
|
||||
*
|
||||
* The callback will get an object "batch" as only argument. On batch, you can call the methods
|
||||
* "call" and "notify" just as if it was a normal $.JsonRpcClient object, and all calls will be
|
||||
* sent as a batch call then the callback is done.
|
||||
*
|
||||
* @fn batch
|
||||
* @memberof $.JsonRpcClient
|
||||
*
|
||||
* @param callback The main function which will get a batch handler to run call and notify on.
|
||||
* @param all_done_cb A callback function to call after all results have been handled.
|
||||
* @param error_cb A callback function to call if there is an error from the server.
|
||||
* Note, that batch calls should always get an overall success, and the
|
||||
* only error
|
||||
*/
|
||||
$.JsonRpcClient.prototype.batch = function(callback, all_done_cb, error_cb) {
|
||||
var batch = new $.JsonRpcClient._batchObject(this, all_done_cb, error_cb);
|
||||
callback(batch);
|
||||
batch._execute();
|
||||
};
|
||||
|
||||
/**
|
||||
* The default getSocket handler.
|
||||
*
|
||||
* @param onmessage_cb The callback to be bound to onmessage events on the socket.
|
||||
*
|
||||
* @fn _getSocket
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
|
||||
$.JsonRpcClient.prototype.socketReady = function() {
|
||||
if (this._ws_socket === null || this._ws_socket.readyState > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
$.JsonRpcClient.prototype.closeSocket = function() {
|
||||
var self = this;
|
||||
if (self.socketReady()) {
|
||||
self._ws_socket.onclose = function (w) {console.log("Closing Socket");};
|
||||
self._ws_socket.close();
|
||||
}
|
||||
};
|
||||
|
||||
$.JsonRpcClient.prototype.loginData = function(params) {
|
||||
var self = this;
|
||||
self.options.login = params.login;
|
||||
self.options.passwd = params.passwd;
|
||||
self.options.loginParams = params.loginParams;
|
||||
self.options.userVariables = params.userVariables;
|
||||
};
|
||||
|
||||
$.JsonRpcClient.prototype.connectSocket = function(onmessage_cb) {
|
||||
var self = this;
|
||||
|
||||
if (self.to) {
|
||||
clearTimeout(self.to);
|
||||
}
|
||||
|
||||
if (!self.socketReady()) {
|
||||
self.authing = false;
|
||||
|
||||
if (self._ws_socket) {
|
||||
delete self._ws_socket;
|
||||
}
|
||||
|
||||
// No socket, or dying socket, let's get a new one.
|
||||
self._ws_socket = new WebSocket(self.options.socketUrl);
|
||||
|
||||
if (self._ws_socket) {
|
||||
// Set up onmessage handler.
|
||||
self._ws_socket.onmessage = onmessage_cb;
|
||||
self._ws_socket.onclose = function (w) {
|
||||
if (!self.ws_sleep) {
|
||||
self.ws_sleep = 1000;
|
||||
}
|
||||
|
||||
if (self.options.onWSClose) {
|
||||
self.options.onWSClose(self);
|
||||
}
|
||||
|
||||
console.error("Websocket Lost " + self.ws_cnt + " sleep: " + self.ws_sleep + "msec");
|
||||
|
||||
self.to = setTimeout(function() {
|
||||
console.log("Attempting Reconnection....");
|
||||
self.connectSocket(onmessage_cb);
|
||||
}, self.ws_sleep);
|
||||
|
||||
self.ws_cnt++;
|
||||
|
||||
if (self.ws_sleep < 3000 && (self.ws_cnt % 10) === 0) {
|
||||
self.ws_sleep += 1000;
|
||||
}
|
||||
};
|
||||
|
||||
// Set up sending of message for when the socket is open.
|
||||
self._ws_socket.onopen = function() {
|
||||
if (self.to) {
|
||||
clearTimeout(self.to);
|
||||
}
|
||||
self.ws_sleep = 1000;
|
||||
self.ws_cnt = 0;
|
||||
if (self.options.onWSConnect) {
|
||||
self.options.onWSConnect(self);
|
||||
}
|
||||
|
||||
var req;
|
||||
// Send the requests.
|
||||
while ((req = $.JsonRpcClient.q.pop())) {
|
||||
self._ws_socket.send(req);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return self._ws_socket ? true : false;
|
||||
};
|
||||
|
||||
$.JsonRpcClient.prototype._getSocket = function(onmessage_cb) {
|
||||
// If there is no ws url set, we don't have a socket.
|
||||
// Likewise, if there is no window.WebSocket.
|
||||
if (this.options.socketUrl === null || !("WebSocket" in window)) return null;
|
||||
|
||||
this.connectSocket(onmessage_cb);
|
||||
|
||||
return this._ws_socket;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue to save messages delivered when websocket is not ready
|
||||
*/
|
||||
$.JsonRpcClient.q = [];
|
||||
|
||||
/**
|
||||
* Internal handler to dispatch a JRON-RPC request through a websocket.
|
||||
*
|
||||
* @fn _wsCall
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
$.JsonRpcClient.prototype._wsCall = function(socket, request, success_cb, error_cb) {
|
||||
var request_json = $.toJSON(request);
|
||||
|
||||
if (socket.readyState < 1) {
|
||||
// The websocket is not open yet; we have to set sending of the message in onopen.
|
||||
self = this; // In closure below, this is set to the WebSocket. Use self instead.
|
||||
$.JsonRpcClient.q.push(request_json);
|
||||
} else {
|
||||
// We have a socket and it should be ready to send on.
|
||||
socket.send(request_json);
|
||||
}
|
||||
|
||||
// Setup callbacks. If there is an id, this is a call and not a notify.
|
||||
if ('id' in request && typeof success_cb !== 'undefined') {
|
||||
this._ws_callbacks[request.id] = { request: request_json, request_obj: request, success_cb: success_cb, error_cb: error_cb };
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal handler for the websocket messages. It determines if the message is a JSON-RPC
|
||||
* response, and if so, tries to couple it with a given callback. Otherwise, it falls back to
|
||||
* given external onmessage-handler, if any.
|
||||
*
|
||||
* @param event The websocket onmessage-event.
|
||||
*/
|
||||
$.JsonRpcClient.prototype._wsOnMessage = function(event) {
|
||||
// Check if this could be a JSON RPC message.
|
||||
var response;
|
||||
|
||||
// Special sub proto
|
||||
if (event.data[0] == "#" && event.data[1] == "S" && event.data[2] == "P") {
|
||||
if (event.data[3] == "U") {
|
||||
this.up_dur = parseInt(event.data.substring(4));
|
||||
} else if (this.speedCB && event.data[3] == "D") {
|
||||
this.down_dur = parseInt(event.data.substring(4));
|
||||
|
||||
var up_kps = (((this.speedBytes * 8) / (this.up_dur / 1000)) / 1024).toFixed(0);
|
||||
var down_kps = (((this.speedBytes * 8) / (this.down_dur / 1000)) / 1024).toFixed(0);
|
||||
|
||||
console.info("Speed Test: Up: " + up_kps + " Down: " + down_kps);
|
||||
this.speedCB(event, { upDur: this.up_dur, downDur: this.down_dur, upKPS: up_kps, downKPS: down_kps });
|
||||
this.speedCB = null;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
response = $.parseJSON(event.data);
|
||||
|
||||
/// @todo Make using the jsonrcp 2.0 check optional, to use this on JSON-RPC 1 backends.
|
||||
|
||||
if (typeof response === 'object' &&
|
||||
'jsonrpc' in response &&
|
||||
response.jsonrpc === '2.0') {
|
||||
|
||||
/// @todo Handle bad response (without id).
|
||||
|
||||
// If this is an object with result, it is a response.
|
||||
if ('result' in response && this._ws_callbacks[response.id]) {
|
||||
// Get the success callback.
|
||||
var success_cb = this._ws_callbacks[response.id].success_cb;
|
||||
|
||||
/*
|
||||
// set the sessid if present
|
||||
if ('sessid' in response.result && !this.options.sessid || (this.options.sessid != response.result.sessid)) {
|
||||
this.options.sessid = response.result.sessid;
|
||||
if (this.options.sessid) {
|
||||
console.log("setting session UUID to: " + this.options.sessid);
|
||||
}
|
||||
}
|
||||
*/
|
||||
// Delete the callback from the storage.
|
||||
delete this._ws_callbacks[response.id];
|
||||
|
||||
// Run callback with result as parameter.
|
||||
success_cb(response.result, this);
|
||||
return;
|
||||
} else if ('error' in response && this._ws_callbacks[response.id]) {
|
||||
// If this is an object with error, it is an error response.
|
||||
|
||||
// Get the error callback.
|
||||
var error_cb = this._ws_callbacks[response.id].error_cb;
|
||||
var orig_req = this._ws_callbacks[response.id].request;
|
||||
|
||||
// if this is an auth request, send the credentials and resend the failed request
|
||||
if (!self.authing && response.error.code == -32000 && self.options.login && self.options.passwd) {
|
||||
self.authing = true;
|
||||
|
||||
this.call("login", { login: self.options.login, passwd: self.options.passwd, loginParams: self.options.loginParams,
|
||||
userVariables: self.options.userVariables},
|
||||
this._ws_callbacks[response.id].request_obj.method == "login" ?
|
||||
function(e) {
|
||||
self.authing = false;
|
||||
console.log("logged in");
|
||||
delete self._ws_callbacks[response.id];
|
||||
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(true, self);
|
||||
}
|
||||
}
|
||||
|
||||
:
|
||||
|
||||
function(e) {
|
||||
self.authing = false;
|
||||
console.log("logged in, resending request id: " + response.id);
|
||||
var socket = self.options.getSocket(self.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
socket.send(orig_req);
|
||||
}
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(true, self);
|
||||
}
|
||||
},
|
||||
|
||||
function(e) {
|
||||
console.log("error logging in, request id:", response.id);
|
||||
delete self._ws_callbacks[response.id];
|
||||
error_cb(response.error, this);
|
||||
if (self.options.onWSLogin) {
|
||||
self.options.onWSLogin(false, self);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Delete the callback from the storage.
|
||||
delete this._ws_callbacks[response.id];
|
||||
|
||||
// Run callback with the error object as parameter.
|
||||
error_cb(response.error, this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// Probably an error while parsing a non json-string as json. All real JSON-RPC cases are
|
||||
// handled above, and the fallback method is called below.
|
||||
console.log("ERROR: "+ err);
|
||||
return;
|
||||
}
|
||||
|
||||
// This is not a JSON-RPC response. Call the fallback message handler, if given.
|
||||
if (typeof this.options.onmessage === 'function') {
|
||||
event.eventData = response;
|
||||
if (!event.eventData) {
|
||||
event.eventData = {};
|
||||
}
|
||||
|
||||
var reply = this.options.onmessage(event);
|
||||
|
||||
if (reply && typeof reply === "object" && event.eventData.id) {
|
||||
var msg = {
|
||||
jsonrpc: "2.0",
|
||||
id: event.eventData.id,
|
||||
result: reply
|
||||
};
|
||||
|
||||
var socket = self.options.getSocket(self.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
socket.send($.toJSON(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/************************************************************************************************
|
||||
* Batch object with methods
|
||||
************************************************************************************************/
|
||||
|
||||
/**
|
||||
* Handling object for batch calls.
|
||||
*/
|
||||
$.JsonRpcClient._batchObject = function(jsonrpcclient, all_done_cb, error_cb) {
|
||||
// Array of objects to hold the call and notify requests. Each objects will have the request
|
||||
// object, and unless it is a notify, success_cb and error_cb.
|
||||
this._requests = [];
|
||||
|
||||
this.jsonrpcclient = jsonrpcclient;
|
||||
this.all_done_cb = all_done_cb;
|
||||
this.error_cb = typeof error_cb === 'function' ? error_cb : function() {};
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* @sa $.JsonRpcClient.prototype.call
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype.call = function(method, params, success_cb, error_cb) {
|
||||
|
||||
if (!params) {
|
||||
params = {};
|
||||
}
|
||||
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
if (!success_cb) {
|
||||
success_cb = function(e){console.log("Success: ", e);};
|
||||
}
|
||||
|
||||
if (!error_cb) {
|
||||
error_cb = function(e){console.log("Error: ", e);};
|
||||
}
|
||||
|
||||
this._requests.push({
|
||||
request : {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params,
|
||||
id : this.jsonrpcclient._current_id++ // Use the client's id series.
|
||||
},
|
||||
success_cb : success_cb,
|
||||
error_cb : error_cb
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* @sa $.JsonRpcClient.prototype.notify
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype.notify = function(method, params) {
|
||||
if (this.options.sessid) {
|
||||
params.sessid = this.options.sessid;
|
||||
}
|
||||
|
||||
this._requests.push({
|
||||
request : {
|
||||
jsonrpc : '2.0',
|
||||
method : method,
|
||||
params : params
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Executes the batched up calls.
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype._execute = function() {
|
||||
var self = this;
|
||||
|
||||
if (this._requests.length === 0) return; // All done :P
|
||||
|
||||
// Collect all request data and sort handlers by request id.
|
||||
var batch_request = [];
|
||||
var handlers = {};
|
||||
var i = 0;
|
||||
var call;
|
||||
var success_cb;
|
||||
var error_cb;
|
||||
|
||||
// If we have a WebSocket, just send the requests individually like normal calls.
|
||||
var socket = self.jsonrpcclient.options.getSocket(self.jsonrpcclient.wsOnMessage);
|
||||
if (socket !== null) {
|
||||
for (i = 0; i < this._requests.length; i++) {
|
||||
call = this._requests[i];
|
||||
success_cb = ('success_cb' in call) ? call.success_cb : undefined;
|
||||
error_cb = ('error_cb' in call) ? call.error_cb : undefined;
|
||||
self.jsonrpcclient._wsCall(socket, call.request, success_cb, error_cb);
|
||||
}
|
||||
|
||||
if (typeof all_done_cb === 'function') all_done_cb(result);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < this._requests.length; i++) {
|
||||
call = this._requests[i];
|
||||
batch_request.push(call.request);
|
||||
|
||||
// If the request has an id, it should handle returns (otherwise it's a notify).
|
||||
if ('id' in call.request) {
|
||||
handlers[call.request.id] = {
|
||||
success_cb : call.success_cb,
|
||||
error_cb : call.error_cb
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
success_cb = function(data) { self._batchCb(data, handlers, self.all_done_cb); };
|
||||
|
||||
// No WebSocket, and no HTTP backend? This won't work.
|
||||
if (self.jsonrpcclient.options.ajaxUrl === null) {
|
||||
throw "$.JsonRpcClient.batch used with no websocket and no http endpoint.";
|
||||
}
|
||||
|
||||
// Send request
|
||||
$.ajax({
|
||||
url : self.jsonrpcclient.options.ajaxUrl,
|
||||
data : $.toJSON(batch_request),
|
||||
dataType : 'json',
|
||||
cache : false,
|
||||
type : 'POST',
|
||||
|
||||
// Batch-requests should always return 200
|
||||
error : function(jqXHR, textStatus, errorThrown) {
|
||||
self.error_cb(jqXHR, textStatus, errorThrown);
|
||||
},
|
||||
success : success_cb
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal helper to match the result array from a batch call to their respective callbacks.
|
||||
*
|
||||
* @fn _batchCb
|
||||
* @memberof $.JsonRpcClient
|
||||
*/
|
||||
$.JsonRpcClient._batchObject.prototype._batchCb = function(result, handlers, all_done_cb) {
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
var response = result[i];
|
||||
|
||||
// Handle error
|
||||
if ('error' in response) {
|
||||
if (response.id === null || !(response.id in handlers)) {
|
||||
// An error on a notify? Just log it to the console.
|
||||
if ('console' in window) console.log(response);
|
||||
} else {
|
||||
handlers[response.id].error_cb(response.error, this);
|
||||
}
|
||||
} else {
|
||||
// Here we should always have a correct id and no error.
|
||||
if (!(response.id in handlers) && 'console' in window) {
|
||||
console.log(response);
|
||||
} else {
|
||||
handlers[response.id].success_cb(response.result, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof all_done_cb === 'function') all_done_cb(result);
|
||||
};
|
||||
|
||||
})(jQuery);
|
10
bigbluebutton-client/resources/prod/lib/jquery.mobile.min.js
vendored
Normal file
10
bigbluebutton-client/resources/prod/lib/jquery.mobile.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2729
bigbluebutton-client/resources/prod/lib/jquery.verto.js
Executable file
2729
bigbluebutton-client/resources/prod/lib/jquery.verto.js
Executable file
File diff suppressed because it is too large
Load Diff
404
bigbluebutton-client/resources/prod/lib/verto_extension.js
Executable file
404
bigbluebutton-client/resources/prod/lib/verto_extension.js
Executable file
@ -0,0 +1,404 @@
|
||||
var callback = function(message){console.log(message);}; // holds the user's callback for a global scope
|
||||
callbacks = {};
|
||||
var callICEConnected = false;
|
||||
var callPurposefullyEnded = false; // used to determine whether the user ended the call or the call was ended from somewhere else outside
|
||||
var callTimeout = null; // function that will run if there is no call established
|
||||
var toDisplayDisconnectCallback = true; // if a call is dropped only display the error the first time
|
||||
var wasCallSuccessful = false; // when the websocket connection is closed this determines whether a call was ever successfully established
|
||||
webcamStream = "webcamStream";
|
||||
window[webcamStream] = null;
|
||||
verto = null;
|
||||
videoTag = null;
|
||||
|
||||
// receives either a string variable holding the name of an actionscript
|
||||
// registered callback, or a javascript function object.
|
||||
// The function will return either the function if it is a javascript Function
|
||||
// or if it is an actionscript string it will return a javascript Function
|
||||
// that when invokved will invoke the actionscript registered callback
|
||||
// and passes along parameters
|
||||
function normalizeCallback(callback) {
|
||||
if (typeof callback == "function") {
|
||||
return callback;
|
||||
} else {
|
||||
return function(args) {
|
||||
document.getElementById("BigBlueButton")[callback](args);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// save a copy of the hangup function registered for the verto object
|
||||
var oldHangup = $.verto.prototype.hangup;
|
||||
// overwrite the verto hangup handler with my own handler
|
||||
$.verto.prototype.hangup = function(callID, userCallback) {
|
||||
console.log("call state callbacks - bye");
|
||||
if (userCallback) {
|
||||
callback = userCallback;
|
||||
}
|
||||
callActive = false;
|
||||
|
||||
if (cur_call) {
|
||||
console.log('call ended ' + cur_call.audioStream.currentTime); // the duration of the call
|
||||
if (callPurposefullyEnded === true) { // the user ended the call themself
|
||||
callback({'status':'ended'});
|
||||
} else {
|
||||
callback({'status':'failed', 'errorcode': 1005}); // Call ended unexpectedly
|
||||
}
|
||||
clearTimeout(callTimeout);
|
||||
cur_call = null;
|
||||
} else {
|
||||
console.log('bye event already received');
|
||||
}
|
||||
// call the original hangup procedure
|
||||
return oldHangup.apply(this, arguments);
|
||||
}
|
||||
|
||||
// main entry point to making a verto call
|
||||
callIntoConference_verto = function(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, videoTag, options, vertoServerCredentials) {
|
||||
window.videoTag = videoTag;
|
||||
// stores the user's callback in the global scope
|
||||
if (userCallback) {
|
||||
callback = userCallback;
|
||||
}
|
||||
if(!isLoggedIntoVerto()) { // start the verto log in procedure
|
||||
// runs when a web socket is disconnected
|
||||
callbacks.onWSClose = function(v, success) {
|
||||
if(wasCallSuccessful) { // a call was established through the websocket
|
||||
if(toDisplayDisconnectCallback) { // will only display the error the first time
|
||||
// the connection was dropped in an already established call
|
||||
console.log("websocket disconnected");
|
||||
callback({'status':'failed', 'errorcode': 1001}); // WebSocket disconnected
|
||||
toDisplayDisconnectCallback = false;
|
||||
}
|
||||
} else {
|
||||
// this callback was triggered and a call was never successfully established
|
||||
console.log("websocket connection could not be established");
|
||||
callback({'status':'failed', 'errorcode': 1002}); // Could not make a WebSocket connection
|
||||
}
|
||||
}
|
||||
// runs when the websocket is successfully created
|
||||
callbacks.onWSLogin = function(v, success) {
|
||||
cur_call = null;
|
||||
ringing = false;
|
||||
console.log("Inside onWSLogin");
|
||||
|
||||
if (success) {
|
||||
console.log("starting call");
|
||||
toDisplayDisconnectCallback = true; // yes, display an error if the socket closes
|
||||
wasCallSuccessful = true; // yes, a call was successfully established through the websocket
|
||||
webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
|
||||
} else {
|
||||
callback({'status':'failed', 'errorcode': '10XX'}); // eror logging verto into freeswitch
|
||||
}
|
||||
}
|
||||
// set up verto
|
||||
// $.verto.init({}, init(videoTag));
|
||||
init(videoTag, vertoServerCredentials);
|
||||
} else {
|
||||
console.log("already logged into verto, going straight to making a call");
|
||||
webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, options);
|
||||
}
|
||||
}
|
||||
|
||||
checkSupport = function(callback) {
|
||||
if(!isWebRTCAvailable_verto()) {
|
||||
callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported
|
||||
}
|
||||
|
||||
if (!navigator.getUserMedia) {
|
||||
navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
|
||||
}
|
||||
|
||||
if (!navigator.getUserMedia){
|
||||
callback({'status': 'failed', 'errorcode': '10XX'}); // getUserMedia not supported in this browser
|
||||
}
|
||||
}
|
||||
|
||||
configStuns = function(callbacks, callback, videoTag, vertoServerCredentials) {
|
||||
console.log("Fetching STUN/TURN server info for Verto initialization");
|
||||
var stunsConfig = {};
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: '/bigbluebutton/api/stuns/'
|
||||
}).done(function(data) {
|
||||
console.log("ajax request done");
|
||||
console.log(data);
|
||||
if(data['response'] && data.response.returncode == "FAILED") {
|
||||
console.error(data.response.message);
|
||||
callback({'status':'failed', 'errorcode': data.response.message});
|
||||
return;
|
||||
}
|
||||
stunsConfig['stunServers'] = ( data['stunServers'] ? data['stunServers'].map(function(data) {
|
||||
return {'url': data['url']};
|
||||
}) : [] );
|
||||
stunsConfig['turnServers'] = ( data['turnServers'] ? data['turnServers'].map(function(data) {
|
||||
return {
|
||||
'urls': data['url'],
|
||||
'username': data['username'],
|
||||
'credential': data['password']
|
||||
};
|
||||
}) : [] );
|
||||
stunsConfig = stunsConfig['stunServers'].concat(stunsConfig['turnServers']);
|
||||
console.log("success got stun data, making verto");
|
||||
makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials);
|
||||
}).fail(function(data, textStatus, errorThrown) {
|
||||
// BBBLog.error("Could not fetch stun/turn servers", {error: textStatus, user: callerIdName, voiceBridge: conferenceVoiceBridge});
|
||||
callback({'status':'failed', 'errorcode': 1009});
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
docall_verto = function(extension, conferenceUsername, conferenceIdNumber, callbacks, options) {
|
||||
console.log(extension + ", " + conferenceUsername + ", " + conferenceIdNumber);
|
||||
|
||||
if (cur_call) { // only allow for one call
|
||||
console.log("Quitting: Call already in progress");
|
||||
return;
|
||||
}
|
||||
// determine the resolution the user chose for webcam video
|
||||
my_check_vid_res();
|
||||
outgoingBandwidth = "default";
|
||||
incomingBandwidth = "default";
|
||||
var useVideo = useCamera = useMic = false;
|
||||
// debugger;
|
||||
if(options.watchOnly) {
|
||||
window.watchOnly = true;
|
||||
window.listenOnly = false;
|
||||
window.joinAudio = false;
|
||||
useVideo = true;
|
||||
useCamera = false;
|
||||
useMic = false;
|
||||
} else if(options.listenOnly) {
|
||||
window.listenOnly = true;
|
||||
window.watchOnly = false;
|
||||
window.joinAudio = false;
|
||||
useVideo = false;
|
||||
useCamera = false;
|
||||
useMic = false;
|
||||
} else if(options.joinAudio) {
|
||||
window.joinAudio = true;
|
||||
window.watchOnly = false;
|
||||
window.listenOnly = false;
|
||||
useVideo = false;
|
||||
useCamera = false;
|
||||
useMic = true;
|
||||
}
|
||||
|
||||
cur_call = verto.newCall({
|
||||
destination_number: extension,
|
||||
caller_id_name: conferenceUsername,
|
||||
caller_id_number: conferenceIdNumber,
|
||||
outgoingBandwidth: outgoingBandwidth,
|
||||
incomingBandwidth: incomingBandwidth,
|
||||
useStereo: true,
|
||||
useVideo: useVideo,
|
||||
useCamera: useCamera,
|
||||
useMic: useMic,
|
||||
dedEnc: false,
|
||||
mirrorInput: false
|
||||
});
|
||||
|
||||
if (callbacks != null) { // add user supplied callbacks to the current call
|
||||
cur_call.rtc.options.callbacks = $.extend(cur_call.rtc.options.callbacks, callbacks);
|
||||
}
|
||||
}
|
||||
|
||||
// check if logged into verto by seeing if there is a ready websocket connection
|
||||
function isLoggedIntoVerto() {
|
||||
return (verto != null ? (ref = verto.rpcClient) != null ? ref.socketReady() : void 0 : void 0);
|
||||
}
|
||||
|
||||
// overwrite and substitute my own init function
|
||||
init = function(videoTag, vertoServerCredentials) {
|
||||
videoTag = window.videoTag;
|
||||
cur_call = null;
|
||||
share_call = null;
|
||||
incomingBandwidth = "default";
|
||||
configStuns(callbacks, callback, videoTag, vertoServerCredentials);
|
||||
}
|
||||
|
||||
// checks whether Google Chrome or Firefox have the WebRTCPeerConnection object
|
||||
function isWebRTCAvailable_verto() {
|
||||
return (typeof window.webkitRTCPeerConnection !== 'undefined' || typeof window.mozRTCPeerConnection !== 'undefined');
|
||||
}
|
||||
|
||||
// exit point for conference
|
||||
function leaveWebRTCVoiceConference_verto() {
|
||||
console.log("Leaving the voice conference");
|
||||
webrtc_hangup_verto();
|
||||
}
|
||||
|
||||
function make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, server, recall, options) {
|
||||
if (userCallback) {
|
||||
callback = userCallback;
|
||||
}
|
||||
callPurposefullyEnded = false;
|
||||
|
||||
// after 15 seconds if a call hasn't been established display error, hangup and logout of verto
|
||||
callTimeout = setTimeout(function() {
|
||||
console.log('Ten seconds without updates sending timeout code');
|
||||
callback({'status':'failed', 'errorcode': 1006}); // Failure on call
|
||||
if (verto != null) {
|
||||
verto.hangup();
|
||||
verto.logout();
|
||||
verto = null;
|
||||
}
|
||||
cur_call = null;
|
||||
}, 10000*1.5);
|
||||
|
||||
var myRTCCallbacks = {
|
||||
onError: function(vertoErrorObject, errorMessage) {
|
||||
console.error("custom callback: onError");
|
||||
console.error(vertoErrorObject);
|
||||
console.error("ERROR:");
|
||||
console.error(errorMessage);
|
||||
if(errorMessage.name === "PermissionDeniedError") { // user denied access to media peripherals
|
||||
console.error("User denied permission/access to hardware");
|
||||
console.error("getUserMedia: failure - ", errorMessage);
|
||||
callback({'status': 'mediafail', 'cause': errorMessage});
|
||||
}
|
||||
cur_call.hangup({cause: "Device or Permission Error"});
|
||||
clearTimeout(callTimeout);
|
||||
},
|
||||
onICEComplete: function(self, candidate) { // ICE candidate negotiation is complete
|
||||
console.log("custom callback: onICEComplete");
|
||||
console.log('Received ICE status changed to completed');
|
||||
if (callICEConnected === false) {
|
||||
callICEConnected = true;
|
||||
if (callActive === true) {
|
||||
callback({'status':'started'});
|
||||
}
|
||||
clearTimeout(callTimeout);
|
||||
}
|
||||
},
|
||||
onStream: function(rtc, stream) { // call has been established
|
||||
console.log("getUserMicMedia: success");
|
||||
callback({'status':'mediasuccess'});
|
||||
console.log("custom callback: stream started");
|
||||
callActive = true;
|
||||
console.log('BigBlueButton call accepted');
|
||||
|
||||
if (callICEConnected === true) {
|
||||
callback({'status':'started'});
|
||||
} else {
|
||||
callback({'status':'waitingforice'});
|
||||
}
|
||||
clearTimeout(callTimeout);
|
||||
}
|
||||
};
|
||||
|
||||
if(isLoggedIntoVerto()) {
|
||||
console.log("Verto is logged into FreeSWITCH, socket is available, making call");
|
||||
callICEConnected = false;
|
||||
|
||||
docall_verto(voiceBridge, conferenceUsername, conferenceIdNumber, myRTCCallbacks, options);
|
||||
|
||||
if(recall === false) {
|
||||
console.log('call connecting');
|
||||
callback({'status': 'connecting'});
|
||||
} else {
|
||||
console.log('call connecting again');
|
||||
}
|
||||
|
||||
callback({'status':'mediarequest'});
|
||||
} else {
|
||||
console.error("Verto is NOT logged into FreeSWITCH, socket is NOT available, abandoning call request");
|
||||
}
|
||||
}
|
||||
|
||||
function makeVerto(callbacks, stunsConfig, videoTag, vertoServerCredentials) {
|
||||
var vertoPort = vertoServerCredentials.vertoPort;
|
||||
var hostName = vertoServerCredentials.hostName;
|
||||
var socketUrl = "wss://" + hostName + ":" + vertoPort;
|
||||
var login = vertoServerCredentials.login;
|
||||
var password = vertoServerCredentials.password;
|
||||
var minWidth = "640";
|
||||
var minHeight = "480";
|
||||
var maxWidth = "1920";
|
||||
var maxHeight = "1080";
|
||||
|
||||
console.log("stuns info is");
|
||||
console.log(stunsConfig);
|
||||
// debugger;
|
||||
verto = new $.verto({
|
||||
login: login,
|
||||
passwd: password,
|
||||
socketUrl: socketUrl,
|
||||
tag: videoTag,
|
||||
ringFile: "sounds/bell_ring2.wav",
|
||||
loginParams: {foo: true, bar: "yes"},
|
||||
useVideo: false,
|
||||
useCamera: false,
|
||||
iceServers: stunsConfig, // use user supplied stun configuration
|
||||
// iceServers: true, // use stun, use default verto configuration
|
||||
}, callbacks);
|
||||
}
|
||||
|
||||
// sets verto to begin using the resolution that the user selected
|
||||
my_check_vid_res = function() {
|
||||
var selectedVideoConstraints = getChosenWebcamResolution();
|
||||
my_real_size(selectedVideoConstraints);
|
||||
|
||||
if (verto) {
|
||||
verto.videoParams({
|
||||
"minWidth": selectedVideoConstraints.constraints.minWidth,
|
||||
"minHeight": selectedVideoConstraints.constraints.minHeight,
|
||||
"maxWidth": selectedVideoConstraints.constraints.maxWidth,
|
||||
"maxHeight": selectedVideoConstraints.constraints.maxHeight,
|
||||
"minFrameRate": selectedVideoConstraints.constraints.minFrameRate,
|
||||
"vertoBestFrameRate": selectedVideoConstraints.constraints.vertoBestFrameRate
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
my_real_size = function(selectedVideoConstraints) {
|
||||
$("#" + window.videoTag).height("100%");
|
||||
$("#" + window.videoTag).width("100%");
|
||||
}
|
||||
|
||||
var RTCPeerConnectionCallbacks = {
|
||||
iceFailed: function(e) {
|
||||
console.log('received ice negotiation failed');
|
||||
callback({'status':'failed', 'errorcode': 1007}); // Failure on call
|
||||
//
|
||||
// TODO unless I do this, the call only lasts for a few seconds.
|
||||
// When I comment out the lines below, it works fine indefinitely
|
||||
// Anton Georgiev Dec 10 2015
|
||||
//
|
||||
//cur_call = null;
|
||||
//verto.hangup();
|
||||
//verto = null;
|
||||
//clearTimeout(callTimeout);
|
||||
}
|
||||
};
|
||||
this.RTCPeerConnectionCallbacks = RTCPeerConnectionCallbacks;
|
||||
|
||||
window.verto_afterStreamPublish = function() {}
|
||||
|
||||
function webrtc_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, userCallback, options) {
|
||||
if (userCallback) {
|
||||
callback = userCallback;
|
||||
}
|
||||
console.log("webrtc_call\n"+voiceBridge + ", " + conferenceUsername + ", " + conferenceIdNumber + ", " + callback);
|
||||
|
||||
if(!isWebRTCAvailable()) {
|
||||
callback({'status': 'failed', 'errorcode': 1003}); // Browser version not supported
|
||||
return;
|
||||
}
|
||||
|
||||
var server = window.document.location.hostname;
|
||||
console.log("user " + conferenceUsername + " calling to " + voiceBridge);
|
||||
if (isLoggedIntoVerto()) {
|
||||
make_call_verto(voiceBridge, conferenceUsername, conferenceIdNumber, callback, "", false, options);
|
||||
}
|
||||
}
|
||||
|
||||
function webrtc_hangup_verto(userCallback) {
|
||||
if (userCallback) {
|
||||
callback = userCallback;
|
||||
}
|
||||
callPurposefullyEnded = true;
|
||||
console.log("Hanging up current session");
|
||||
if(verto) {
|
||||
verto.hangup(false, callback);
|
||||
}
|
||||
}
|
214
bigbluebutton-client/resources/prod/lib/verto_extension_share.js
Executable file
214
bigbluebutton-client/resources/prod/lib/verto_extension_share.js
Executable file
@ -0,0 +1,214 @@
|
||||
var deskshareStream = "deskshareStream";
|
||||
window[deskshareStream] = null;
|
||||
this.share_call = null;
|
||||
|
||||
// final entry point for sharing from Chrome.
|
||||
// already have the resolution and constraints chosen
|
||||
var configDeskshareFromChrome = function(videoTag, callbacks, extensionId, resolutionConstruction) {
|
||||
// do initial check for extension
|
||||
getChromeExtensionStatus(extensionId, function(status) {
|
||||
if (status != "installed-enabled") {
|
||||
callbacks.onError({'status': 'failed', 'errorcode': 2001});
|
||||
console.error("No chrome Extension");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// bring up Chrome screen picker
|
||||
getScreenConstraints(function(error, screen_constraints) {
|
||||
if(error) {
|
||||
callbacks.onError({'status': 'failed', 'errorcode': 2021});
|
||||
return console.error(error);
|
||||
}
|
||||
|
||||
screen_constraints = resolutionConstruction(screen_constraints);
|
||||
window.firefoxDesksharePresent = false;
|
||||
doCall(screen_constraints, videoTag, callbacks);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// entry point for Chrome HTML5 sharing
|
||||
// connects with html5 client libraries to retrieve a selected resolution
|
||||
var configDeskshareFromChromeHTML5 = function(videoTag, callbacks, extensionId) {
|
||||
var resolutionConstruction = function(screen_constraints) {
|
||||
console.log("modifying video quality");
|
||||
var selectedDeskshareResolution = getChosenDeskshareResolution(); // this is the video profile the user chose
|
||||
my_real_size(selectedDeskshareResolution);
|
||||
var selectedDeskshareConstraints = getDeskshareConstraintsFromResolution(selectedDeskshareResolution, screen_constraints); // convert to a valid constraints object
|
||||
console.log(selectedDeskshareConstraints);
|
||||
return selectedDeskshareConstraints.video.mandatory;
|
||||
};
|
||||
configDeskshareFromChrome(videoTag, callbacks, extensionId, resolutionConstruction);
|
||||
};
|
||||
|
||||
// entry point when desksharing using Google Chrome via the flash Client
|
||||
// currently uses a default preset resolution in place of a resolution picker
|
||||
// prepares the constraints and passes off to generic Chrome handler
|
||||
var configDeskshareFromChromeFlash = function(videoTag, callbacks, extensionId) {
|
||||
var resolutionConstruction = function(screen_constraints) {
|
||||
// BigBlueButton low
|
||||
var getDeskshareConstraints = function(constraints) {
|
||||
return {
|
||||
"audio": false,
|
||||
"video": {
|
||||
"mandatory": {
|
||||
"maxWidth": 160,
|
||||
"maxHeight": 120,
|
||||
"chromeMediaSource": constraints.mandatory.chromeMediaSource,
|
||||
"chromeMediaSourceId": constraints.mandatory.chromeMediaSourceId,
|
||||
"minFrameRate": 10,
|
||||
},
|
||||
"optional": []
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
console.log("not modifying video quality");
|
||||
var selectedDeskshareConstraints = getDeskshareConstraints(screen_constraints); // convert to a valid constraints object
|
||||
console.log(selectedDeskshareConstraints);
|
||||
return selectedDeskshareConstraints.video.mandatory;
|
||||
};
|
||||
configDeskshareFromChrome(videoTag, callbacks, extensionId, resolutionConstruction);
|
||||
};
|
||||
|
||||
// final entry point for Firefox sharing
|
||||
var configDeskshareFromFirefox = function(screen_constraints, videoTag, callbacks) {
|
||||
// bypass all the default gUM calls inside jquery.FSRTC.js to use my own
|
||||
window.firefoxDesksharePresent = true;
|
||||
// the gUM args to invoke the Firefox screen picker
|
||||
var screen_constraints = {
|
||||
video: {
|
||||
"mozMediaSource": 'window',
|
||||
"mediaSource": 'window',
|
||||
}
|
||||
};
|
||||
// register the callback to the window namespace to be available in jquery.FSRTC.js
|
||||
window.firefoxDesksharePresentErrorCallback = callbacks.onError;
|
||||
doCall(screen_constraints, videoTag, callbacks);
|
||||
};
|
||||
|
||||
var configDeskshareFromFirefoxFlash = function(screen_constraints, videoTag, callbacks) {
|
||||
console.log("deskshare from firefox flash");
|
||||
configDeskshareFromFirefox(screen_constraints, videoTag, callbacks);
|
||||
};
|
||||
|
||||
var configDeskshareFromFirefoxHTML5 = function(screen_constraints, videoTag, callbacks) {
|
||||
console.log("deskshare from firefox html5");
|
||||
configDeskshareFromFirefox(screen_constraints, videoTag, callbacks);
|
||||
};
|
||||
|
||||
function endScreenshare(loggingCallback, onSuccess) {
|
||||
console.log("endScreenshare");
|
||||
if (share_call) {
|
||||
console.log("a screenshare call is active. Hanging up");
|
||||
share_call.hangup();
|
||||
share_call = null;
|
||||
normalizeCallback(onSuccess)();
|
||||
} else {
|
||||
console.log("a screenshare call is NOT active. Doing nothing");
|
||||
}
|
||||
normalizeCallback(loggingCallback)({'status':'success', 'message': 'screenshare ended'});
|
||||
}
|
||||
|
||||
function startScreenshare(loggingCallback, videoTag, vertoServerCredentials, extensionId, modifyResolution, onSuccess, onFail) {
|
||||
onSuccess = normalizeCallback(onSuccess);
|
||||
onFail = normalizeCallback(onFail);
|
||||
loggingCallback = normalizeCallback(loggingCallback);
|
||||
console.log("startScreenshare");
|
||||
if(!isLoggedIntoVerto()) { // start the verto log in procedure
|
||||
// runs when the websocket is successfully created
|
||||
callbacks.onWSLogin = function(v, success) {
|
||||
startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail);
|
||||
loggingCallback({'status':'success', 'message': 'screenshare started'});
|
||||
console.log("logged in. starting screenshare");
|
||||
}
|
||||
// set up verto
|
||||
init(window.videoTag, vertoServerCredentials);
|
||||
} else {
|
||||
console.log("already logged into verto, going straight to making a call");
|
||||
startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail);
|
||||
loggingCallback({'status':'success', 'message': 'screenshare started'});
|
||||
}
|
||||
}
|
||||
|
||||
function startScreenshareAfterLogin(loggingCallback, videoTag, extensionId, modifyResolution, onSuccess, onFail) {
|
||||
if (share_call) {
|
||||
return;
|
||||
}
|
||||
|
||||
outgoingBandwidth = incomingBandwidth = "default";
|
||||
var sharedev = "screen";
|
||||
|
||||
if (sharedev !== "screen") {
|
||||
console.log("Attempting Screen Capture with non-screen device....");
|
||||
|
||||
BBB.getMyUserInfo(function (retData){
|
||||
share_call = verto.newCall({
|
||||
destination_number: retData.voiceBridge + "-screen",
|
||||
caller_id_name: retData.myUsername + " (Screen)",
|
||||
caller_id_number: retData.myUserID + " (screen)",
|
||||
outgoingBandwidth: outgoingBandwidth,
|
||||
incomingBandwidth: incomingBandwidth,
|
||||
useCamera: sharedev,
|
||||
useVideo: true,
|
||||
screenShare: true,
|
||||
dedEnc: false,
|
||||
mirrorInput: false
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var callbacks = {
|
||||
onError: normalizeCallback(onFail),
|
||||
onICEComplete: function(self, candidate) { // ICE candidate negotiation is complete
|
||||
console.log("custom callback: onICEComplete");
|
||||
normalizeCallback(onSuccess)(candidate);
|
||||
}
|
||||
};
|
||||
|
||||
// determine if Firefox or Chrome
|
||||
// for now the only difference is that html5 has a resolution dialog
|
||||
if (!!navigator.mozGetUserMedia) {
|
||||
if (modifyResolution) {
|
||||
configDeskshareFromFirefoxHTML5(null, videoTag, callbacks);
|
||||
} else {
|
||||
configDeskshareFromFirefoxFlash(null, videoTag, callbacks);
|
||||
}
|
||||
} else if (!!window.chrome) {
|
||||
if (modifyResolution) {
|
||||
configDeskshareFromChromeHTML5(videoTag, callbacks, extensionId);
|
||||
} else {
|
||||
configDeskshareFromChromeFlash(videoTag, callbacks, extensionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function doCall(screen_constraints, videoTag, callbacks) {
|
||||
console.log("\n\n\nhere are the screen_constraints\n\n\n");
|
||||
console.log(screen_constraints);
|
||||
window.listenOnly = false;
|
||||
window.watchOnly = false;
|
||||
window.joinAudio = true;
|
||||
|
||||
BBB.getMyUserInfo(function (retData){
|
||||
var callParams = {
|
||||
destination_number: retData.voiceBridge + "-screen",
|
||||
caller_id_name: retData.myUsername + " (Screen)",
|
||||
caller_id_number: retData.myUserID + " (screen)",
|
||||
outgoingBandwidth: outgoingBandwidth,
|
||||
incomingBandwidth: incomingBandwidth,
|
||||
videoParams: screen_constraints,
|
||||
useVideo: true,
|
||||
screenShare: true,
|
||||
dedEnc: true,
|
||||
mirrorInput: true,
|
||||
};
|
||||
|
||||
if (videoTag != null) {
|
||||
callParams.tag = videoTag;
|
||||
}
|
||||
share_call = verto.newCall(callParams);
|
||||
share_call.rtc.options.callbacks = $.extend(share_call.rtc.options.callbacks, callbacks);
|
||||
});
|
||||
}
|
@ -20,45 +20,46 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-->
|
||||
|
||||
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
|
||||
xmlns:maps="org.bigbluebutton.modules.deskShare.maps.*"
|
||||
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
|
||||
xmlns:maps="org.bigbluebutton.modules.deskshare.maps.*"
|
||||
implements="org.bigbluebutton.common.IBigBlueButtonModule"
|
||||
creationComplete="onCreationComplete()" xmlns:maps1="org.bigbluebutton.modules.deskshare.maps.*">
|
||||
|
||||
<maps1:DeskshareEventMap id="deskshareGlobalEventMap" />
|
||||
creationComplete="onCreationComplete()">
|
||||
|
||||
<maps:DeskshareEventMap id="javaDeskshareEventMap" />
|
||||
<maps:WebRTCDeskshareEventMap id="webRTCDeskshareEventMap" />
|
||||
|
||||
<mx:Script>
|
||||
<![CDATA[
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.common.IBigBlueButtonModule;
|
||||
import org.bigbluebutton.modules.deskshare.events.ModuleEvent;
|
||||
|
||||
|
||||
private static const LOGGER:ILogger = getClassLogger(DeskShareModule);
|
||||
|
||||
private var _moduleName:String = "Desk Share";
|
||||
private var _attributes:Object;
|
||||
|
||||
|
||||
private var globalDispatcher:Dispatcher = new Dispatcher();;
|
||||
|
||||
|
||||
private function onCreationComplete():void{
|
||||
LOGGER.debug("DeskShareModule initialized");
|
||||
}
|
||||
|
||||
|
||||
public function get moduleName():String{
|
||||
return _moduleName;
|
||||
}
|
||||
|
||||
|
||||
public function get uri():String{
|
||||
return _attributes.uri;
|
||||
}
|
||||
|
||||
|
||||
public function get username():String{
|
||||
return _attributes.username;
|
||||
}
|
||||
|
||||
|
||||
public function get mode():String{
|
||||
if (_attributes.mode == null){
|
||||
_attributes.mode = "LIVE";
|
||||
|
@ -1,13 +1,13 @@
|
||||
/**
|
||||
* 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.
|
||||
@ -22,19 +22,19 @@ package org.bigbluebutton.modules.deskshare.events
|
||||
|
||||
public class RecordingStatusEvent extends Event
|
||||
{
|
||||
public static const DESKSHARE_RECORD_EVENT = "DESKSHARE_RECORD_EVENT";
|
||||
|
||||
public static const RECORD_STOPPED_EVENT = "DESKSHARE_RECORD_STOPPED_EVENT";
|
||||
public static const RECORD_STARTED_EVENT = "DESKSHARE_RECORD_STARTED_EVENT";
|
||||
public static const RECORD_UPDATED_EVENT = "DESKSHARE_RECORD_UPDATED_EVENT";
|
||||
public static const RECORD_ERROR_EVENT = "DESKSHARE_RECORD_ERROR_EVENT";
|
||||
|
||||
public static const DESKSHARE_RECORD_EVENT:String = "DESKSHARE_RECORD_EVENT";
|
||||
|
||||
public static const RECORD_STOPPED_EVENT:String = "DESKSHARE_RECORD_STOPPED_EVENT";
|
||||
public static const RECORD_STARTED_EVENT:String = "DESKSHARE_RECORD_STARTED_EVENT";
|
||||
public static const RECORD_UPDATED_EVENT:String = "DESKSHARE_RECORD_UPDATED_EVENT";
|
||||
public static const RECORD_ERROR_EVENT:String = "DESKSHARE_RECORD_ERROR_EVENT";
|
||||
|
||||
public var status:String;
|
||||
|
||||
|
||||
public function RecordingStatusEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,14 @@
|
||||
package org.bigbluebutton.modules.deskshare.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
|
||||
public class UseJavaModeCommand extends Event
|
||||
{
|
||||
public static const USE_JAVA_MODE:String = "use Java to join deskshare event";
|
||||
|
||||
public function UseJavaModeCommand()
|
||||
{
|
||||
super(USE_JAVA_MODE, true, false);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.modules.deskshare.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
|
||||
public class WebRTCShareWindowEvent extends Event
|
||||
{
|
||||
public static const CLOSE:String = "WebRTC Deskshare Share Window Close Event";
|
||||
|
||||
public function WebRTCShareWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/**
|
||||
* 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.modules.deskshare.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
|
||||
public class WebRTCStreamEvent extends Event
|
||||
{
|
||||
public static const START:String = "WebRTC Deskshare Stream Started Event";
|
||||
public static const STOP:String = "WebRTC Deskshare Stream Stopped Event";
|
||||
|
||||
public var videoWidth:Number = 0;
|
||||
public var videoHeight:Number = 0;
|
||||
|
||||
public function WebRTCStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* 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.modules.deskshare.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
|
||||
public class WebRTCViewStreamEvent extends Event
|
||||
{
|
||||
public static const START:String = "WebRTC Start Viewing Stream Event";
|
||||
public static const STOP:String = "WebRTC Stop Viewing Stream Event";
|
||||
|
||||
public var videoWidth:Number = 0;
|
||||
public var videoHeight:Number = 0;
|
||||
public var rtmp:String = null;
|
||||
|
||||
public function WebRTCViewStreamEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
JSLog.warn("creating a WebRTCViewStreamEvent event " + type, null);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* 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.modules.deskshare.events
|
||||
{
|
||||
import flash.events.Event;
|
||||
|
||||
public class WebRTCViewWindowEvent extends Event
|
||||
{
|
||||
public static const CLOSE:String = "WebRTC Deskshare View Window Close Event";
|
||||
|
||||
public function WebRTCViewWindowEvent(type:String, bubbles:Boolean=true, cancelable:Boolean=false)
|
||||
{
|
||||
super(type, bubbles, cancelable);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
119
bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/DeskshareManager.as
Normal file → Executable file
119
bigbluebutton-client/src/org/bigbluebutton/modules/deskshare/managers/DeskshareManager.as
Normal file → Executable file
@ -1,13 +1,13 @@
|
||||
/**
|
||||
* 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.
|
||||
@ -20,15 +20,19 @@
|
||||
package org.bigbluebutton.modules.deskshare.managers
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.main.events.MadePresenterEvent;
|
||||
import org.bigbluebutton.modules.deskshare.events.UseJavaModeCommand;
|
||||
import org.bigbluebutton.modules.deskshare.model.DeskshareOptions;
|
||||
import org.bigbluebutton.modules.deskshare.services.DeskshareService;
|
||||
|
||||
public class DeskshareManager {
|
||||
import org.bigbluebutton.modules.deskshare.utils.JavaCheck;
|
||||
import org.bigbluebutton.modules.deskshare.utils.BrowserCheck;
|
||||
import flash.external.ExternalInterface;
|
||||
|
||||
public class DeskshareManager {
|
||||
private static const LOGGER:ILogger = getClassLogger(DeskshareManager);
|
||||
|
||||
private var publishWindowManager:PublishWindowManager;
|
||||
@ -38,65 +42,66 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
private var service:DeskshareService;
|
||||
private var globalDispatcher:Dispatcher;
|
||||
private var sharing:Boolean = false;
|
||||
|
||||
private var usingJava:Boolean = true;
|
||||
|
||||
public function DeskshareManager() {
|
||||
service = new DeskshareService();
|
||||
globalDispatcher = new Dispatcher();
|
||||
publishWindowManager = new PublishWindowManager(service);
|
||||
viewWindowManager = new ViewerWindowManager(service);
|
||||
toolbarButtonManager = new ToolbarButtonManager();
|
||||
viewWindowManager = new ViewerWindowManager(service);
|
||||
toolbarButtonManager = new ToolbarButtonManager();
|
||||
}
|
||||
|
||||
|
||||
public function handleStartModuleEvent(module:DeskShareModule):void {
|
||||
LOGGER.debug("Deskshare Module starting");
|
||||
this.module = module;
|
||||
this.module = module;
|
||||
service.handleStartModuleEvent(module);
|
||||
|
||||
|
||||
if (UsersUtil.amIPresenter()) {
|
||||
initDeskshare();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function handleStopModuleEvent():void {
|
||||
LOGGER.debug("Deskshare Module stopping");
|
||||
publishWindowManager.stopSharing();
|
||||
viewWindowManager.stopViewing();
|
||||
viewWindowManager.stopViewing();
|
||||
service.disconnect();
|
||||
}
|
||||
|
||||
|
||||
public function handleStreamStoppedEvent():void {
|
||||
LOGGER.debug("Sending deskshare stopped command");
|
||||
service.stopSharingDesktop(module.getRoom(), module.getRoom());
|
||||
}
|
||||
|
||||
|
||||
public function handleStreamStartedEvent(videoWidth:Number, videoHeight:Number):void {
|
||||
LOGGER.debug("Sending startViewing command");
|
||||
service.sendStartViewingNotification(videoWidth, videoHeight);
|
||||
}
|
||||
|
||||
|
||||
public function handleStartedViewingEvent(stream:String):void {
|
||||
LOGGER.debug("handleStartedViewingEvent [{0}]", [stream]);
|
||||
service.sendStartedViewingNotification(stream);
|
||||
}
|
||||
|
||||
|
||||
private function initDeskshare():void {
|
||||
sharing = false;
|
||||
var option:DeskshareOptions = new DeskshareOptions();
|
||||
option.parseOptions();
|
||||
if (option.autoStart) {
|
||||
var options:DeskshareOptions = new DeskshareOptions();
|
||||
options.parseOptions();
|
||||
if (options.autoStart) {
|
||||
handleStartSharingEvent(true);
|
||||
}
|
||||
if(option.showButton){
|
||||
if(options.showButton){
|
||||
toolbarButtonManager.addToolbarButton();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function handleMadePresenterEvent(e:MadePresenterEvent):void {
|
||||
LOGGER.debug("Got MadePresenterEvent ");
|
||||
initDeskshare();
|
||||
}
|
||||
|
||||
|
||||
public function handleMadeViewerEvent(e:MadePresenterEvent):void{
|
||||
LOGGER.debug("Got MadeViewerEvent ");
|
||||
toolbarButtonManager.removeToolbarButton();
|
||||
@ -105,35 +110,65 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
}
|
||||
sharing = false;
|
||||
}
|
||||
|
||||
|
||||
public function handleStartSharingEvent(autoStart:Boolean):void {
|
||||
LOGGER.debug("DeskshareManager::handleStartSharingEvent");
|
||||
//toolbarButtonManager.disableToolbarButton();
|
||||
toolbarButtonManager.startedSharing();
|
||||
var option:DeskshareOptions = new DeskshareOptions();
|
||||
option.parseOptions();
|
||||
publishWindowManager.startSharing(option.publishURI , option.useTLS , module.getRoom(), autoStart, option.autoFullScreen);
|
||||
sharing = true;
|
||||
LOGGER.debug("DeskshareManager::handleStartSharingEvent (Java)");
|
||||
var options:DeskshareOptions = new DeskshareOptions();
|
||||
options.parseOptions();
|
||||
|
||||
// falling back to java, or we can't use WebRTC
|
||||
if (autoStart || (options.useWebRTCIfAvailable && !BrowserCheck.isWebRTCSupported())) {
|
||||
if (BrowserCheck.isUsingLessThanChrome38OnMac()) {
|
||||
usingJava = false;
|
||||
publishWindowManager.startSharing(options.publishURI , options.useTLS , module.getRoom(), autoStart, options.autoFullScreen);
|
||||
} else {
|
||||
var javaIssue:String = JavaCheck.checkJava();
|
||||
|
||||
if (javaIssue != null) {
|
||||
if (BrowserCheck.isChrome42OrHigher()) {
|
||||
usingJava = false;
|
||||
publishWindowManager.startSharing(options.publishURI , options.useTLS , module.getRoom(), autoStart, options.autoFullScreen);
|
||||
} else {
|
||||
usingJava = false;
|
||||
publishWindowManager.startSharing(options.publishURI , options.useTLS , module.getRoom(), autoStart, options.autoFullScreen);
|
||||
}
|
||||
} else {
|
||||
usingJava = true;
|
||||
toolbarButtonManager.startedSharing();
|
||||
|
||||
publishWindowManager.startSharing(options.publishURI , options.useTLS , module.getRoom(), autoStart, options.autoFullScreen);
|
||||
sharing = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// using WebRTC or not a fallback case
|
||||
usingJava = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function handleShareWindowCloseEvent():void {
|
||||
//toolbarButtonManager.enableToolbarButton();
|
||||
publishWindowManager.handleShareWindowCloseEvent();
|
||||
sharing = false;
|
||||
toolbarButtonManager.stopedSharing();
|
||||
}
|
||||
|
||||
|
||||
public function handleViewWindowCloseEvent():void {
|
||||
LOGGER.debug("Received stop viewing command");
|
||||
viewWindowManager.handleViewWindowCloseEvent();
|
||||
LOGGER.debug("Received stop viewing command");
|
||||
viewWindowManager.handleViewWindowCloseEvent();
|
||||
}
|
||||
|
||||
|
||||
public function handleStreamStartEvent(videoWidth:Number, videoHeight:Number):void{
|
||||
if (sharing) return;
|
||||
LOGGER.debug("Received start vieweing command");
|
||||
if (!usingJava) { return; }
|
||||
if (sharing) { return; }
|
||||
LOGGER.debug("Received start viewing command");
|
||||
viewWindowManager.startViewing(module.getRoom(), videoWidth, videoHeight);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public function handleUseJavaModeCommand():void {
|
||||
usingJava = true;
|
||||
handleStartSharingEvent(true);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
private var globalDispatcher:Dispatcher;
|
||||
private var service:DeskshareService;
|
||||
private var buttonShownOnToolbar:Boolean = false;
|
||||
|
||||
private var isOpen:Boolean = false;
|
||||
|
||||
// Timer to auto-publish webcam. We need this timer to delay
|
||||
// the auto-publishing until after the Viewers's window has loaded
|
||||
// to receive the publishing events. Otherwise, the user joining next
|
||||
@ -53,12 +54,18 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
}
|
||||
|
||||
public function stopSharing():void {
|
||||
if (shareWindow != null) shareWindow.stopSharing();
|
||||
if (shareWindow != null) {
|
||||
shareWindow.stopSharing();
|
||||
isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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]);
|
||||
|
||||
if (isOpen) {
|
||||
return;
|
||||
}
|
||||
isOpen = true;
|
||||
shareWindow = new DesktopPublishWindow();
|
||||
shareWindow.initWindow(service.getConnection(), uri , useTLS , room, autoStart, autoFullScreen);
|
||||
shareWindow.visible = true;
|
||||
@ -72,7 +79,7 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
autoPublishTimer = new Timer(2000, 1);
|
||||
autoPublishTimer.addEventListener(TimerEvent.TIMER, autopublishTimerHandler);
|
||||
autoPublishTimer.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function autopublishTimerHandler(event:TimerEvent):void {
|
||||
@ -82,14 +89,16 @@ package org.bigbluebutton.modules.deskshare.managers
|
||||
public function handleShareWindowCloseEvent():void {
|
||||
closeWindow(shareWindow);
|
||||
}
|
||||
|
||||
private function openWindow(window:IBbbModuleWindow):void {
|
||||
|
||||
private function openWindow(window:IBbbModuleWindow):void {
|
||||
isOpen = true;
|
||||
var event:OpenWindowEvent = new OpenWindowEvent(OpenWindowEvent.OPEN_WINDOW_EVENT);
|
||||
event.window = window;
|
||||
globalDispatcher.dispatchEvent(event);
|
||||
}
|
||||
|
||||
private function closeWindow(window:IBbbModuleWindow):void {
|
||||
isOpen = false;
|
||||
var event:CloseWindowEvent = new CloseWindowEvent(CloseWindowEvent.CLOSE_WINDOW_EVENT);
|
||||
event.window = window;
|
||||
globalDispatcher.dispatchEvent(event);
|
||||
|
@ -0,0 +1,261 @@
|
||||
/**
|
||||
* 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.modules.deskshare.managers
|
||||
{
|
||||
import com.asfusion.mate.events.Dispatcher;
|
||||
|
||||
import flash.external.ExternalInterface;
|
||||
|
||||
import org.as3commons.logging.api.ILogger;
|
||||
import org.as3commons.logging.api.getClassLogger;
|
||||
import org.bigbluebutton.core.UsersUtil;
|
||||
import org.bigbluebutton.core.managers.UserManager;
|
||||
import org.bigbluebutton.main.events.MadePresenterEvent;
|
||||
import org.bigbluebutton.modules.deskshare.events.UseJavaModeCommand;
|
||||
import org.bigbluebutton.modules.deskshare.events.WebRTCViewStreamEvent;
|
||||
import org.bigbluebutton.modules.deskshare.model.DeskshareOptions;
|
||||
import org.bigbluebutton.modules.deskshare.events.ShareWindowEvent;
|
||||
import org.bigbluebutton.modules.deskshare.services.WebRTCDeskshareService;
|
||||
import org.bigbluebutton.modules.deskshare.utils.BrowserCheck;
|
||||
import org.bigbluebutton.main.api.JSLog;
|
||||
|
||||
public class WebRTCDeskshareManager {
|
||||
private static const LOGGER:ILogger = getClassLogger(WebRTCDeskshareManager);
|
||||
|
||||
private var publishWindowManager:WebRTCPublishWindowManager;
|
||||
private var viewWindowManager:WebRTCViewerWindowManager;
|
||||
private var toolbarButtonManager:ToolbarButtonManager;
|
||||
private var module:DeskShareModule;
|
||||
private var service:WebRTCDeskshareService;
|
||||
private var globalDispatcher:Dispatcher;
|
||||
private var sharing:Boolean = false;
|
||||
private var usingWebRTC:Boolean = false;
|
||||
private var chromeExtensionKey:String = null;
|
||||
private var vertoServerCredentials:Object = new Object();
|
||||
|
||||
public function WebRTCDeskshareManager() {
|
||||
service = new WebRTCDeskshareService();
|
||||
globalDispatcher = new Dispatcher();
|
||||
publishWindowManager = new WebRTCPublishWindowManager(service);
|
||||
viewWindowManager = new WebRTCViewerWindowManager(service);
|
||||
}
|
||||
|
||||
public function handleStartModuleEvent(module:DeskShareModule):void {
|
||||
LOGGER.debug("WebRTC Deskshare Module starting");
|
||||
this.module = module;
|
||||
service.handleStartModuleEvent(module);
|
||||
|
||||
if (UsersUtil.amIPresenter()) {
|
||||
initDeskshare();
|
||||
}
|
||||
}
|
||||
|
||||
public function handleStopModuleEvent():void {
|
||||
LOGGER.debug("WebRTC Deskshare Module stopping");
|
||||
|
||||
publishWindowManager.stopSharing();
|
||||
viewWindowManager.stopViewing();
|
||||
service.disconnect();
|
||||
}
|
||||
|
||||
/*presenter stopped their program stream*/
|
||||
public function handleStreamStoppedEvent():void {
|
||||
LOGGER.debug("DeskshareManager::handleStreamStoppedEvent Sending deskshare stopped command");
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStreamStoppedEvent", {});
|
||||
stopWebRTCDeskshare();
|
||||
}
|
||||
|
||||
/*viewer being told there is no more stream*/
|
||||
public function handleStreamStopEvent(args:Object):void {
|
||||
LOGGER.debug("WebRTCDeskshareManager::handleStreamStopEvent");
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStreamStopEvent", {});
|
||||
viewWindowManager.handleViewWindowCloseEvent();
|
||||
}
|
||||
|
||||
private function stopWebRTCDeskshare():void {
|
||||
LOGGER.debug("DeskshareManager::stopWebRTCDeskshare");
|
||||
viewWindowManager.stopViewing();
|
||||
/* stopping WebRTC deskshare. Alert DeskshareManager to reset toolbar */
|
||||
globalDispatcher.dispatchEvent(new ShareWindowEvent(ShareWindowEvent.CLOSE));
|
||||
|
||||
if (ExternalInterface.available) {
|
||||
var loggingCallback:Function = function(args:Object):void {LOGGER.debug(args); JSLog.warn("loggingCallback", args)};
|
||||
var onSuccess:Function = function():void { LOGGER.debug("onSuccess"); JSLog.warn("onSuccess - as", {})};
|
||||
ExternalInterface.addCallback("loggingCallback", loggingCallback);
|
||||
ExternalInterface.addCallback("onSuccess", onSuccess);
|
||||
ExternalInterface.call("endScreenshare", "loggingCallback", "onSuccess");
|
||||
} else {
|
||||
LOGGER.error("Error! ExternalInterface not available (webrtcDeskshare)");
|
||||
}
|
||||
}
|
||||
|
||||
private function startWebRTCDeskshare():void {
|
||||
LOGGER.debug("DeskshareManager::startWebRTCDeskshare");
|
||||
|
||||
var result:String;
|
||||
if (ExternalInterface.available) {
|
||||
var loggingCallback:Function = function(args:Object):void {LOGGER.debug(args); JSLog.warn("loggingCallback", args)};
|
||||
ExternalInterface.addCallback("loggingCallback", loggingCallback);
|
||||
var videoTag:String = "localVertoVideo";
|
||||
var modifyResolution:Boolean = false;
|
||||
// register these callbacks
|
||||
var onSuccess:Function = function():void { LOGGER.debug("onSuccess"); JSLog.warn("onSuccess - as", {})};
|
||||
ExternalInterface.addCallback("onSuccess", onSuccess);
|
||||
var onFail:Function = function(args:Object):void {
|
||||
JSLog.warn("onFail - as", args);
|
||||
JSLog.warn("WebRTCDeskshareManager::startWebRTCDeskshare - falling back to java", {});
|
||||
globalDispatcher.dispatchEvent(new UseJavaModeCommand())
|
||||
};
|
||||
ExternalInterface.addCallback("onFail", onFail);
|
||||
JSLog.warn("calling startScreenshare", {});
|
||||
result = ExternalInterface.call("startScreenshare", "loggingCallback", videoTag, vertoServerCredentials, chromeExtensionKey, modifyResolution, "onSuccess", "onFail");
|
||||
}
|
||||
}
|
||||
|
||||
private function initDeskshare():void {
|
||||
sharing = false;
|
||||
var options:DeskshareOptions = new DeskshareOptions();
|
||||
options.parseOptions();
|
||||
if (options.chromeExtensionKey) {
|
||||
chromeExtensionKey = options.chromeExtensionKey;
|
||||
}
|
||||
if (options.vertoPort) {
|
||||
vertoServerCredentials.vertoPort = options.vertoPort;
|
||||
}
|
||||
if (options.hostName) {
|
||||
vertoServerCredentials.hostName = options.hostName;
|
||||
}
|
||||
if (options.login) {
|
||||
vertoServerCredentials.login = options.login;
|
||||
}
|
||||
if (options.password) {
|
||||
vertoServerCredentials.password = options.password;
|
||||
}
|
||||
if (options.autoStart) {
|
||||
handleStartSharingEvent(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleMadePresenterEvent(e:MadePresenterEvent):void {
|
||||
LOGGER.debug("Got MadePresenterEvent ");
|
||||
initDeskshare();
|
||||
}
|
||||
|
||||
public function handleMadeViewerEvent(e:MadePresenterEvent):void{
|
||||
LOGGER.debug("Got MadeViewerEvent ");
|
||||
if (sharing) {
|
||||
publishWindowManager.stopSharing();
|
||||
stopWebRTCDeskshare();
|
||||
}
|
||||
sharing = false;
|
||||
}
|
||||
|
||||
private function canIUseVertoOnThisBrowser(onFailure:Function, onSuccess:Function):void {
|
||||
LOGGER.debug("DeskshareManager::canIUseVertoOnThisBrowser");
|
||||
var options:DeskshareOptions = new DeskshareOptions();
|
||||
options.parseOptions();
|
||||
|
||||
if (options.useWebRTCIfAvailable && BrowserCheck.isWebRTCSupported()) {
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent WebRTC Supported", {});
|
||||
if (BrowserCheck.isFirefox()) {
|
||||
onSuccess("Firefox, lets try");
|
||||
} else {
|
||||
if (chromeExtensionKey != null) {
|
||||
/*toolbarButtonManager.startedSharing();*/
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent chrome extension key exists - ", chromeExtensionKey);
|
||||
if (ExternalInterface.available) {
|
||||
var success:Function = function(status:String):void {
|
||||
ExternalInterface.addCallback("callback", null);
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent inside onSuccess", {});
|
||||
if (status == "installed-enabled") {
|
||||
JSLog.warn("Chrome Extension exists", {});
|
||||
onSuccess("worked");
|
||||
} else {
|
||||
onFailure("No Chrome Extension");
|
||||
}
|
||||
};
|
||||
ExternalInterface.addCallback("callback", success);
|
||||
ExternalInterface.call("getChromeExtensionStatus", chromeExtensionKey, "callback");
|
||||
}
|
||||
} else {
|
||||
onFailure("No chromeExtensionKey in config.xml");
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
onFailure("Web browser doesn't support WebRTC");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*handle start sharing event*/
|
||||
public function handleStartSharingEvent(autoStart:Boolean):void {
|
||||
LOGGER.debug("WebRTCDeskshareManager::handleStartSharingEvent");
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent", {});
|
||||
var onFailure:Function = function(message:String):void {
|
||||
JSLog.warn(message, {});
|
||||
usingWebRTC = false;
|
||||
// send out event to fallback to Java
|
||||
JSLog.warn("WebRTCDeskshareManager::handleStartSharingEvent - falling back to java", {});
|
||||
globalDispatcher.dispatchEvent(new UseJavaModeCommand());
|
||||
return;
|
||||
};
|
||||
|
||||
var onSuccess:Function = function(message:String):void {
|
||||
JSLog.warn(message, {});
|
||||
usingWebRTC = true;
|
||||
startWebRTCDeskshare();
|
||||
};
|
||||
|
||||
canIUseVertoOnThisBrowser(onFailure, onSuccess);
|
||||
}
|
||||
|
||||
public function handleShareWindowCloseEvent():void {
|
||||
publishWindowManager.handleShareWindowCloseEvent();
|
||||
sharing = false;
|
||||
stopWebRTCDeskshare();
|
||||
}
|
||||
|
||||
public function handleViewWindowCloseEvent():void {
|
||||
LOGGER.debug("Received stop viewing command");
|
||||
JSLog.warn("WebRTCDeskshareManager::handleViewWindowCloseEvent", {});
|
||||
viewWindowManager.handleViewWindowCloseEvent();
|
||||
}
|
||||
|
||||
public function handleStreamStartEvent(e:WebRTCViewStreamEvent):void{
|
||||
// if (!usingWebRTC) { return; } //TODO this was causing issues
|
||||
if (sharing) return; //TODO must uncomment this for the non-webrtc desktop share
|
||||
var isPresenter:Boolean = UserManager.getInstance().getConference().amIPresenter;
|
||||
LOGGER.debug("Received start viewing command when isPresenter==[{0}]",[isPresenter]);
|
||||
|
||||
if(isPresenter) {
|
||||
publishWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
|
||||
} else {
|
||||
viewWindowManager.startViewing(e.rtmp, e.videoWidth, e.videoHeight);
|
||||
}
|
||||
|
||||
sharing = true; //TODO must uncomment this for the non-webrtc desktop share
|
||||
}
|
||||
|
||||
public function handleUseJavaModeCommand():void {
|
||||
usingWebRTC = false;
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user