Merge branch 'master' of https://github.com/bigbluebutton/bigbluebutton into meteor-react

This commit is contained in:
Oleksandr Zhurbenko 2016-03-11 11:26:54 -08:00
commit cdbc951e56
87 changed files with 488 additions and 4201 deletions

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ bbb-lti/bin
bbb-lti/lti-0.1.1.war
bbb-lti/make.sh
bbb-lti/deploy.sh
bbb-lti/target*
bbb-api-demo/bin*
bbb-api-demo/.classpath
bbb-api-demo/.project

View File

@ -0,0 +1,28 @@
package org.bigbluebutton.core.pubsub.receivers;
public class ReceivedMessage {
private final String pattern;
private final String channel;
private final String message;
public ReceivedMessage(String pattern, String channel, String message) {
this.pattern = pattern;
this.channel = channel;
this.message = message;
}
public String getPattern() {
return pattern;
}
public String getChannel() {
return channel;
}
public String getMessage() {
return message;
}
}

View File

@ -2,10 +2,24 @@ package org.bigbluebutton.core.pubsub.receivers;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import org.bigbluebutton.core.api.IBigBlueButtonInGW;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RedisMessageReceiver {
private static final Logger log = LoggerFactory.getLogger(RedisMessageReceiver.class);
private BlockingQueue<ReceivedMessage> receivedMessages = new LinkedBlockingQueue<ReceivedMessage>();
private volatile boolean processMessage = false;
private final Executor msgProcessorExec = Executors.newSingleThreadExecutor();
private final Executor runExec = Executors.newSingleThreadExecutor();
private List<MessageHandler> receivers;
private IBigBlueButtonInGW bbbGW;
@ -13,6 +27,7 @@ public class RedisMessageReceiver {
this.bbbGW = bbbGW;
receivers = new ArrayList<MessageHandler>();
setupReceivers();
start();
}
private void setupReceivers() {
@ -39,8 +54,44 @@ public class RedisMessageReceiver {
}
public void handleMessage(String pattern, String channel, String message) {
for (MessageHandler l : receivers) {
l.handleMessage(pattern, channel, message);
}
ReceivedMessage rm = new ReceivedMessage(pattern, channel, message);
receivedMessages.add(rm);
}
public void stop() {
processMessage = false;
}
public void start() {
try {
processMessage = true;
Runnable messageProcessor = new Runnable() {
public void run() {
while (processMessage) {
try {
ReceivedMessage msg = receivedMessages.take();
processMessage(msg);
} catch (InterruptedException e) {
log.warn("Error while taking received message from queue.");
}
}
}
};
msgProcessorExec.execute(messageProcessor);
} catch (Exception e) {
log.error("Error subscribing to channels: " + e.getMessage());
}
}
private void processMessage(final ReceivedMessage msg) {
Runnable task = new Runnable() {
public void run() {
for (MessageHandler l : receivers) {
l.handleMessage(msg.getPattern(), msg.getChannel(), msg.getMessage());
}
}
};
runExec.execute(task);
}
}

View File

@ -234,6 +234,21 @@ trait UsersApp {
}
}
def makeSurePresenterIsAssigned(user: UserVO): Unit = {
if (user.presenter) {
/* The current presenter has left the meeting. Find a moderator and make
* him presenter. This way, if there is a moderator in the meeting, there
* will always be a presenter.
*/
val moderator = usersModel.findAModerator()
moderator.foreach { mod =>
log.info("Presenter left meeting. meetingId=" + mProps.meetingID + " userId=" + user.userID
+ ". Making user=[" + mod.userID + "] presenter.")
assignNewPresenter(mod.userID, mod.name, mod.userID)
}
}
}
def handleEjectUserFromMeeting(msg: EjectUserFromMeeting) {
usersModel.getUser(msg.userId) foreach { user =>
if (user.voiceUser.joined) {
@ -244,6 +259,8 @@ trait UsersApp {
usersModel.removeUser(msg.userId)
usersModel.removeRegUser(msg.userId)
makeSurePresenterIsAssigned(user)
log.info("Ejecting user from meeting. meetingId=" + mProps.meetingID + " userId=" + msg.userId)
outGW.send(new UserEjectedFromMeeting(mProps.meetingID, mProps.recorded, msg.userId, msg.ejectedBy))
outGW.send(new DisconnectUser(mProps.meetingID, msg.userId))
@ -373,19 +390,7 @@ trait UsersApp {
log.info("User left meeting. meetingId=" + mProps.meetingID + " userId=" + u.userID + " user=" + u)
outGW.send(new UserLeft(msg.meetingID, mProps.recorded, u))
if (u.presenter) {
/* The current presenter has left the meeting. Find a moderator and make
* him presenter. This way, if there is a moderator in the meeting, there
* will always be a presenter.
*/
val moderator = usersModel.findAModerator()
moderator.foreach { mod =>
log.info("Presenter left meeting. meetingId=" + mProps.meetingID + " userId=" + u.userID
+ ". Making user=[" + mod.userID + "] presenter.")
assignNewPresenter(mod.userID, mod.name, mod.userID)
}
}
makeSurePresenterIsAssigned(u)
val vu = u.voiceUser
if (vu.joined || u.listenOnly) {

2
bbb-api-demo/src/main/webapp/create.jsp Executable file → Normal file
View File

@ -277,7 +277,7 @@ function mycallback() {
// the meeting was created.
String joinURL = getJoinURLViewer(request.getParameter("username"), request.getParameter("meetingID"));
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

2
bbb-api-demo/src/main/webapp/demo1.jsp Executable file → Normal file
View File

@ -90,7 +90,7 @@ if (request.getParameterMap().isEmpty()) {
String joinURL = getJoinURL(request.getParameter("username"), "Demo Meeting", "false", null, null, null);
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

2
bbb-api-demo/src/main/webapp/demo10.jsp Executable file → Normal file
View File

@ -265,7 +265,7 @@ with BigBlueButton; if not, If not, see <http://www.gnu.org/licenses/>.
//
String welcomeMsg = "<br>Welcome to %%CONFNAME%%!<br><br>For help see our <a href=\"event:http://www.bigbluebutton.org/content/videos\"><u>tutorial videos</u></a>.<br><br>To join the voice bridge for this meeting click the headset icon in the upper-left <b>(you can mute yourself in the Listeners window)</b>.<br><br>This meeting is being recorded.";
String joinURL = getJoinURL(username, meetingID, "true", welcomeMsg, metadata, null);
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";

View File

@ -235,6 +235,9 @@
</script>
</head>
<body>
<div>
<audio id="remote-media" autoplay="autoplay"></audio>
</div>
<div id="controls">
<button type="button" onclick="getMeetingID()">Get MeetingID</button>

View File

@ -97,11 +97,11 @@ if (request.getParameterMap().isEmpty()) {
String joinURL = getJoinMeetingURL(request.getParameter("username"), "Demo Meeting", "mp", clientURL );
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";
window.location.href="<%=joinURL%>";
</script>
<%

4
bbb-api-demo/src/main/webapp/demo12.jsp Executable file → Normal file
View File

@ -160,7 +160,7 @@ Author: Jesus Federico <jesus@123it.ca>
//Set skin
Element skinElement = (Element) doc.getElementsByTagName("skinning").item(0);
skinElement.setAttribute("url", "http://" + getBigBlueButtonIP() + "/client/branding/css/" + param_Skin + ".css.swf" );
skinElement.setAttribute("url", "https://" + getBigBlueButtonIP() + "/client/branding/css/" + param_Skin + ".css.swf" );
//Set layout
Element layoutElement = (Element) doc.getElementsByTagName("layout").item(0);
@ -210,7 +210,7 @@ Author: Jesus Federico <jesus@123it.ca>
String url = BigBlueButtonURL.replace("bigbluebutton/","demo/");
String joinURL = getJoinURLwithDynamicConfigXML(username, confname, configXML);
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("https://") || joinURL.startsWith("https://")) {
%>
<h2>Customized sessions using a dynamic config.xml, submit</h2>

2
bbb-api-demo/src/main/webapp/demo2.jsp Executable file → Normal file
View File

@ -110,7 +110,7 @@ Author: Fred Dixon <ffdixon@bigbluebutton.org>
// String joinURL = getJoinURL(username, meetingID, "false", "<br>Welcome to course: %%CONFNAME%%.<br>", null, preUploadPDF );
String joinURL = getJoinURL(username, meetingID, "false", null, null, null );
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

2
bbb-api-demo/src/main/webapp/demo6.jsp Executable file → Normal file
View File

@ -144,7 +144,7 @@ Author: Fred Dixon <ffdixon@bigbluebutton.org>
String preUploadPDF = "<?xml version='1.0' encoding='UTF-8'?><modules><module name='presentation'><document url='"+url+"pdfs/matterhorn.pdf'/></module></modules>";
String joinURL = getJoinURL(username, confname, "true", null, metadata, preUploadPDF);
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

2
bbb-api-demo/src/main/webapp/demo7.jsp Executable file → Normal file
View File

@ -101,7 +101,7 @@
String joinURL = getJoinURLXML(uname, "Join and Upload example", welcomeMsg, xml );
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";

2
bbb-api-demo/src/main/webapp/demo8.jsp Executable file → Normal file
View File

@ -78,7 +78,7 @@
String meetingID = presentationFileName.replace("pdfs/","").replace(".pdf","").replace(".pptx","");
String joinURL = getJoinURL(username, meetingID, "false", welcomeMsg, null, xml );
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">
window.location.href="<%=joinURL%>";

2
bbb-api-demo/src/main/webapp/demo9.jsp Executable file → Normal file
View File

@ -92,7 +92,7 @@ if (request.getParameterMap().isEmpty()) {
//
String joinURL = getJoinURL(request.getParameter("username"), "Demo Meeting", "false", null, null);
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

View File

@ -100,7 +100,7 @@ if (request.getParameterMap().isEmpty()) {
// redirect towards the html5 client which is waiting for the following parameters
String html5url = ip + "/html5client/" + meetingId + "/" + userId + "/" + authToken;
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

3
bbb-api-demo/src/main/webapp/demo_mozilla_persona.jsp Executable file → Normal file
View File

@ -132,7 +132,6 @@ function loggedIn(res){
Gson gson = new Gson();
HashMap<String,String> map = gson.fromJson(jsonResp, new TypeToken<Map<String, String>>() {}.getType());
if(map.get("status").equalsIgnoreCase("okay")){
joinURL = getJoinURL(map.get("email"), "Demo Meeting", "false", null, null, null);
}
@ -143,7 +142,7 @@ function loggedIn(res){
// String preUploadPDF = "<?xml version='1.0' encoding='UTF-8'?><modules><module name='presentation'><document url='"+url+"pdfs/sample.pdf'/></module></modules>";
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

View File

@ -86,14 +86,6 @@ if (request.getParameterMap().isEmpty()) {
<FORM id="form1" NAME="form1" METHOD="GET" ACTION="#">
<table cellpadding="5" cellspacing="5" style="width: 400px; ">
<tbody>
<tr>
<td>&nbsp;</td>
<td style="text-align: left ">
<a href="#" id="google" title="Sign-in with Google OpenID">
<img src="images/google.png" alt="Sign in"></br>
</a>
</td>
</tr>
<tr>
<td>&nbsp;</td>
<td style="text-align: left ">
@ -147,7 +139,7 @@ if (request.getParameterMap().isEmpty()) {
Authentication authentication = manager.getAuthentication(request, mac_key, alias);
String joinURL = getJoinURL(authentication.getFullname(), "Demo Meeting", null, null, null, null );
if (joinURL.startsWith("http://")) {
if (joinURL.startsWith("http://") || joinURL.startsWith("https://")) {
%>
<script language="javascript" type="text/javascript">

View File

@ -32,12 +32,15 @@ bigbluebuttonSalt=bbb_salt
# e.g. localhost or localhost:port
ltiEndPoint=localhost
# The list of consumers allowed to access this lti service.
# Format: {consumerId1:sharedSecret1}
# Format: {consumerId1:sharedSecret1,consumerId2:sharedSecret2,consumerIdN:sharedSecretN}
##ltiConsumers=bbb:bbb_salt
ltiConsumers=bbb:welcome
# The mode used to interact with BigBlueButton
# Format: [<simple>|extended]
ltiMode=extended
# Defines if LTI credentials are required
# Format: [false|<true>]
ltiRestrictedAccess=true
#----------------------------------------------------
# Inject configuration values into BigbluebuttonSrvice beans
@ -49,4 +52,5 @@ beans.bigbluebuttonService.salt=${bigbluebuttonSalt}
beans.ltiService.endPoint=${ltiEndPoint}
beans.ltiService.consumers=${ltiConsumers}
beans.ltiService.mode=${ltiMode}
beans.ltiService.restrictedAccess=${ltiRestrictedAccess}

View File

@ -54,17 +54,26 @@ class ToolController {
ltiService.logParameters(params)
if( request.post ){
def endPoint = getScheme() + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null? "." + params.get("format"): "")
def scheme = request.isSecure()? "https": "http"
def endPoint = scheme + "://" + ltiService.endPoint + "/" + grailsApplication.metadata['app.name'] + "/" + params.get("controller") + (params.get("format") != null? "." + params.get("format"): "")
log.info "endPoint: " + endPoint
Map<String, String> result = new HashMap<String, String>()
ArrayList<String> missingParams = new ArrayList<String>()
if (hasAllRequiredParams(params, missingParams)) {
def sanitizedParams = sanitizePrametersForBaseString(params)
def consumer = ltiService.getConsumer(params.get(Parameter.CONSUMER_ID))
if (consumer != null) {
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
if (checkValidSignature(params.get(REQUEST_METHOD), endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))) {
log.debug "The message has a valid signature."
if ( !ltiService.hasRestrictedAccess() || consumer != null) {
if (ltiService.hasRestrictedAccess() ) {
log.debug "Found consumer with key " + consumer.get("key") //+ " and sharedSecret " + consumer.get("secret")
}
if (!ltiService.hasRestrictedAccess() || checkValidSignature(params.get(REQUEST_METHOD), endPoint, consumer.get("secret"), sanitizedParams, params.get(Parameter.OAUTH_SIGNATURE))) {
if (!ltiService.hasRestrictedAccess() ) {
log.debug "Access not restricted, valid signature is not required."
} else {
log.debug "The message has a valid signature."
}
def mode = params.containsKey(Parameter.CUSTOM_MODE)? params.get(Parameter.CUSTOM_MODE): ltiService.mode
if( !"extended".equals(mode) ) {
@ -77,11 +86,13 @@ class ToolController {
result = doJoinMeeting(params)
}
}
} else {
log.debug "The message has NOT a valid signature."
result.put("resultMessageKey", "InvalidSignature")
result.put("resultMessage", "Invalid signature (" + params.get(Parameter.OAUTH_SIGNATURE) + ").")
}
} else {
result.put("resultMessageKey", "ConsumerNotFound")
result.put("resultMessage", "Consumer with id = " + params.get(Parameter.CONSUMER_ID) + " was not found.")
@ -311,12 +322,12 @@ class ToolController {
log.debug "Checking for required parameters"
boolean hasAllParams = true
if ( !params.containsKey(Parameter.CONSUMER_ID) ) {
if ( ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.CONSUMER_ID) ) {
missingParams.add(Parameter.CONSUMER_ID);
hasAllParams = false;
}
if ( !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
if ( ltiService.hasRestrictedAccess() && !params.containsKey(Parameter.OAUTH_SIGNATURE)) {
missingParams.add(Parameter.OAUTH_SIGNATURE);
hasAllParams = false;
}
@ -341,22 +352,27 @@ class ToolController {
private boolean checkValidSignature(String method, String url, String conSecret, Properties postProp, String signature) {
def validSignature = false
try {
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
//log.debug "OAuthMessage oam = " + oam.toString()
if ( ltiService.hasRestrictedAccess() ) {
try {
OAuthMessage oam = new OAuthMessage(method, url, postProp.entrySet())
//log.debug "OAuthMessage oam = " + oam.toString()
HMAC_SHA1 hmac = new HMAC_SHA1()
//log.debug "HMAC_SHA1 hmac = " + hmac.toString()
HMAC_SHA1 hmac = new HMAC_SHA1()
//log.debug "HMAC_SHA1 hmac = " + hmac.toString()
hmac.setConsumerSecret(conSecret)
hmac.setConsumerSecret(conSecret)
log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
log.debug "Calculated: " + calculatedSignature + " Received: " + signature
log.debug "Base Message String = [ " + hmac.getBaseString(oam) + " ]\n"
String calculatedSignature = hmac.getSignature(hmac.getBaseString(oam))
log.debug "Calculated: " + calculatedSignature + " Received: " + signature
validSignature = calculatedSignature.equals(signature)
} catch( Exception e ) {
log.debug "Exception error: " + e.message
validSignature = calculatedSignature.equals(signature)
} catch( Exception e ) {
log.debug "Exception error: " + e.message
}
} else {
validSignature = true
}
return validSignature
@ -398,18 +414,4 @@ class ToolController {
return cartridge
}
private String getScheme(){
def scheme
if ( request.isSecure() ) {
scheme = 'https'
} else {
scheme = request.getHeader("scheme")
if ( scheme == null || !(scheme == 'http' || scheme == 'https') ) {
scheme = 'http'
}
}
return scheme
}
}

View File

@ -21,6 +21,7 @@ import java.util.Map;
import javax.crypto.spec.SecretKeySpec
import javax.crypto.Mac
import org.apache.commons.codec.binary.Base64
class LtiService {
@ -30,8 +31,7 @@ class LtiService {
def endPoint = "localhost"
def consumers = "demo:welcome"
def mode = "simple"
def ssl_enabled
def restrictedAccess = "true"
Map<String, String> consumerMap
@ -92,45 +92,55 @@ class LtiService {
return Base64.encodeBase64URLSafeString(signBytes)
}
def logParameters(Object params) {
log.debug "----------------------------------"
for( param in params ) log.debug "${param.getKey()}=${param.getValue()}"
log.debug "----------------------------------"
def logParameters(Object params, boolean debug = false) {
def divider = "----------------------------------"
Map<String, String> ordered_params = new LinkedHashMap<String, String>(params)
ordered_params = ordered_params.sort {it.key}
if( debug ) log.debug divider else log.info divider
for( param in ordered_params ) {
if( debug ) {
log.debug "${param.getKey()}=${param.getValue()}"
} else {
log.info "${param.getKey()}=${param.getValue()}"
}
}
if( debug ) log.debug divider else log.info divider
}
def boolean isSSLEnabled(String query) {
if ( ssl_enabled == null ) {
ssl_enabled = false
log.debug("Pinging SSL connection")
def ssl_enabled = false
try {
// open connection
StringBuilder urlStr = new StringBuilder(query)
URL url = new URL(urlStr.toString())
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection()
httpConnection.setUseCaches(false)
httpConnection.setDoOutput(true)
httpConnection.setRequestMethod("HEAD")
httpConnection.setConnectTimeout(5000)
httpConnection.connect()
log.debug("Pinging SSL connection")
try {
// open connection
StringBuilder urlStr = new StringBuilder(query)
URL url = new URL(urlStr.toString())
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection()
httpConnection.setUseCaches(false)
httpConnection.setDoOutput(true)
httpConnection.setRequestMethod("HEAD")
httpConnection.setConnectTimeout(5000)
httpConnection.connect()
int responseCode = httpConnection.getResponseCode()
if (responseCode == HttpURLConnection.HTTP_OK) {
ssl_enabled = true
} else {
log.debug("HTTPERROR: Message=" + "BBB server responded with HTTP status code " + responseCode)
}
int responseCode = httpConnection.getResponseCode()
if (responseCode == HttpURLConnection.HTTP_OK) {
ssl_enabled = true
} else {
log.debug("HTTPERROR: Message=" + "BBB server responded with HTTP status code " + responseCode)
}
} catch(IOException e) {
log.debug("IOException: Message=" + e.getMessage())
} catch(IllegalArgumentException e) {
log.debug("IllegalArgumentException: Message=" + e.getMessage())
} catch(Exception e) {
log.debug("Exception: Message=" + e.getMessage())
}
}
} catch(IOException e) {
log.debug("IOException: Message=" + e.getMessage())
} catch(IllegalArgumentException e) {
log.debug("IllegalArgumentException: Message=" + e.getMessage())
} catch(Exception e) {
log.debug("Exception: Message=" + e.getMessage())
}
return ssl_enabled
}
def boolean hasRestrictedAccess() {
return Boolean.parseBoolean(this.restrictedAccess);
}
}

View File

@ -1,12 +1,12 @@
# Handle request to bbb-web running within Tomcat. This is for
# the BBB-API and Presentation.
# Handle request to bbb-web running within Tomcat. This is for
# the BBB-API and Presentation.
location /lti {
proxy_pass http://127.0.0.1:8080;
proxy_redirect default;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Scheme $scheme;
proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
# Allow 30M uploaded presentation document.
# Allow 30M uploaded presentation document.
client_max_body_size 30m;
client_body_buffer_size 128k;

View File

@ -27,6 +27,7 @@ import org.red5.server.api.scope.IScope;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.bigbluebutton.red5.BigBlueButtonSession;
import org.bigbluebutton.red5.Constants;
import org.bigbluebutton.red5.pubsub.MessagePublisher;
@ -59,6 +60,12 @@ public class ParticipantsService {
String meetingId = scope.getName();
String userId = (String) msg.get("userId");
String emojiStatus = (String) msg.get("emojiStatus");
if (StringUtils.isEmpty(emojiStatus)) {
log.warn("Invalid EmojiStatus from client: meetingId=" + meetingId + ", userId=" + userId + ",emoji=" + emojiStatus);
// Set emojiStatus=none if passed is null.
emojiStatus = "none";
}
red5InGW.userEmojiStatus(meetingId, userId, emojiStatus);
}

View File

@ -116,9 +116,9 @@ Button, .logoutButtonStyle, .chatSendButtonStyle, .helpLinkButtonStyle, .cameraD
highlightAlphas: 0.4, 0;
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
themeColor: haloBlue;
fontFamily: Arial;
@ -180,10 +180,10 @@ Button, .logoutButtonStyle, .chatSendButtonStyle, .helpLinkButtonStyle, .cameraD
DataGrid {
backgroundColor: #e1e2e5;
rollOverColor: #f3f3f3;
textRollOverColor: #5e5f63;
textRollOverColor: #3f3f41;
selectionColor: #e1e2e5;
color: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textSelectedColor: #3f3f41;
dropShadowEnabled: false;
fontFamily: arial;
fontSize: 11;
@ -191,7 +191,7 @@ DataGrid {
}
.mydataGridHeaderStyle {
color: #5e5f63;
color: #3f3f41;
fontFamily: arial;
fontSize: 12;
}
@ -209,9 +209,9 @@ DataGrid {
highlightAlphas: 0.4, 0;
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
themeColor: haloBlue;
fontFamily: Arial;
@ -303,9 +303,9 @@ DataGrid {
highlightAlphas: 0.4, 0;
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
themeColor: haloBlue;
fontFamily: Arial;
@ -363,16 +363,16 @@ DataGrid {
}
.presentationZoomSliderLabelStyle {
color: #5e5f63;
color: #3f3f41;
fontFamily: Arial;
}
.deskshareControlButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 17;
}
@ -380,9 +380,9 @@ DataGrid {
.videoMuteButtonStyle, .videoUnmutedButtonStyle, .videoSwitchPresenterButtonStyle, .videoEjectUserButtonStyle, .videoPrivateChatButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 17;
}
@ -392,8 +392,8 @@ DataGrid {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #ffffff;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 17;
icon: Embed('assets/images/webcam-muted.png');
@ -445,9 +445,9 @@ TitleWindow {
.presentationUploadChooseFileButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
}
@ -455,9 +455,9 @@ TitleWindow {
.presentationUploadCancelButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
}
@ -465,9 +465,9 @@ TitleWindow {
.presentationUploadShowButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
}
@ -475,9 +475,9 @@ TitleWindow {
.presentationUploadDeleteButtonStyle {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
icon: Embed('assets/images/trash.png');
@ -536,9 +536,9 @@ TitleWindow {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
rollOverColor: #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
}
@ -547,9 +547,9 @@ TitleWindow {
fillAlphas: 1, 1, 1, 1;
fillColors: #fefeff, #e1e2e5, #ffffff, #eeeeee;
rollOverColor: #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
icon: Embed('assets/images/headset.png');
@ -559,9 +559,9 @@ TitleWindow {
fillAlphas: 1, 1, 1, 1;
fillColors: #ffffff, #eeeeee, #ffffff, #eeeeee;
rollOverColor: #eeeeee;
color: #5e5f63;
textRollOverColor: #5e5f63;
textSelectedColor: #5e5f63;
color: #3f3f41;
textRollOverColor: #3f3f41;
textSelectedColor: #3f3f41;
borderColor: #b9babc;
cornerRadius: 5;
}
@ -741,7 +741,7 @@ MDIWindow { /*None of the following properties are overridden by the MDIWindow c
}
.mdiWindowTitle {
color: #5e5f63;
color: #3f3f41;
fontFamily: Arial;
fontSize: 12;
fontWeight: bold;

View File

@ -418,6 +418,7 @@
<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}/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"/>

View File

@ -152,11 +152,7 @@ bbb.users.usersGrid.statusItemRenderer = Status
bbb.users.usersGrid.statusItemRenderer.changePresenter = Click To Make Presenter
bbb.users.usersGrid.statusItemRenderer.presenter = Presenter
bbb.users.usersGrid.statusItemRenderer.moderator = Moderator
bbb.users.usersGrid.statusItemRenderer.raiseHand = Hand Raised
bbb.users.usersGrid.statusItemRenderer.happy = Happy
bbb.users.usersGrid.statusItemRenderer.unhappy = Unhappy
bbb.users.usersGrid.statusItemRenderer.confused = Confused
bbb.users.usersGrid.statusItemRenderer.clearStatus = Clear my status
bbb.users.usersGrid.statusItemRenderer.clearStatus = Clear status
bbb.users.usersGrid.statusItemRenderer.viewer = Viewer
bbb.users.usersGrid.statusItemRenderer.streamIcon.toolTip = Sharing webcam.
bbb.users.usersGrid.statusItemRenderer.presIcon.toolTip = Is Presenter.

View File

@ -170,7 +170,7 @@
</div>
</div>
</div>
<button id="enterFlash" type="button" class="visually-hidden" onclick="startFlashFocus();" value="Click to focus the client"></button>
<div id="clientReady" aria-atomic="false" aria-live="polite"></div>
<button id="enterFlash" type="button" class="visually-hidden" onclick="startFlashFocus();">Set focus to client</button>
<div id="clientReady" aria-atomic="false" aria-live="polite" class="visually-hidden"></div>
</body>
</html>

View File

@ -1,23 +1,3 @@
(function($){
$.fn.bigbluebutton = function(options){
$('body').append('<div id="speechArea" style="visibility:hidden"></div>');
function sendSpeechMessage(){
var speech = $('#speechArea');
if (speech.html() !== ""){
document.getElementById('BigBlueButton').sendChatMessage(speech.html());
speech.html("");
}
setTimeout(sendSpeechMessage, 300);
}
sendSpeechMessage();
}
})(jQuery);
$(function () { $.fn.bigbluebutton(); });
$(function () {
setTimeout(function(){
$('#BigBlueButton').focus();

View File

@ -88,7 +88,6 @@ package org.bigbluebutton.main.model
}
if (deskshare){generalResource.push('bbb.shortcutkey.share.desktop');}
if (webcam){generalResource.push('bbb.shortcutkey.share.webcam');}
if (polling){generalResource.push('bbb.shortcutkey.polling.buttonClick');}
generalResource.push('bbb.shortcutkey.shortcutWindow');
generalResource.push('bbb.shortcutkey.logout');

View File

@ -269,8 +269,6 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
public function openShortcutHelpWindow(e:Event = null):void{
if (scWindow == null) {
scWindow = new ShortcutHelpWindow();
scWindow.width = 300;
scWindow.height = 300;
}
if (scWindow.minimized)
@ -280,7 +278,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
scWindow.restore();
mdiCanvas.windowManager.add(scWindow);
mdiCanvas.windowManager.absPos(scWindow, mdiCanvas.width/2 - 150, mdiCanvas.height/2 - 150);
mdiCanvas.windowManager.absPos(scWindow, mdiCanvas.width/2 - scWindow.width/2, mdiCanvas.height/2 - scWindow.height/2);
scWindow.focusHead();
}

View File

@ -24,6 +24,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns:common="org.bigbluebutton.common.*"
showCloseButton="true"
height="350"
width="430"
initialize="init()"
creationComplete="onCreationComplete()"
xmlns:mate="http://mate.asfusion.com/"
@ -256,7 +258,7 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
</mx:ComboBox>
<mx:DataGrid id="keyList" draggableColumns="false" dataProvider="{shownKeys}" width="100%" height="100%">
<mx:columns>
<mx:DataGridColumn dataField="shortcut" headerText="{ResourceUtil.getInstance().getString('bbb.shortcuthelp.headers.shortcut')}"/>
<mx:DataGridColumn dataField="shortcut" width="150" headerText="{ResourceUtil.getInstance().getString('bbb.shortcuthelp.headers.shortcut')}"/>
<mx:DataGridColumn dataField="func" headerText="{ResourceUtil.getInstance().getString('bbb.shortcuthelp.headers.function')}"/>
</mx:columns>
</mx:DataGrid>

View File

@ -58,6 +58,11 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
private function onCreationComplete():void{
users = UserManager.getInstance().getConference().users;
if (!chatOptions.privateEnabled) {
usersList.includeInLayout = usersList.visible = false;
}
if (fontSizes.indexOf(chatOptions.fontSize) != -1) {
cmbFontSize.selectedItem = chatOptions.fontSize;
changeFontSize(); // have to manually call it because the change event doesn't fire
@ -107,8 +112,13 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
* For the Options tab focus the users list instead of the text area.
**/
public function focusToTextMessageArea():void {
if (usersList.visible) {
usersList.setFocus();
usersList.drawFocus(true);
} else {
cmbFontSize.setFocus();
cmbFontSize.drawFocus(true);
}
}
private function lockSettingsChanged(e:Event):void {

View File

@ -91,10 +91,8 @@ with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
openChatBoxFor(PUBLIC_CHAT_USERID, true);
makePublicChatUncloseable();
if (chatOptions.privateEnabled) {
createAddTabBox();
makeAddPrivateChatUncloseable();
}
createAddTabBox();
makeAddPrivateChatUncloseable();
ResourceUtil.getInstance().addEventListener(Event.CHANGE, localeChanged); // Listen for locale changing

View File

@ -8,6 +8,7 @@ package org.bigbluebutton.modules.layout.services
import org.as3commons.logging.api.ILogger;
import org.as3commons.logging.api.getClassLogger;
import org.bigbluebutton.core.BBB;
import org.bigbluebutton.core.UsersUtil;
import org.bigbluebutton.main.events.ModuleLoadEvent;
import org.bigbluebutton.main.model.users.IMessageListener;
import org.bigbluebutton.modules.layout.events.LayoutEvent;
@ -48,8 +49,9 @@ package org.bigbluebutton.modules.layout.services
/*
* the application of the first layout should be delayed to avoid
* strange movements of the windows before set the correct position
* TDJ: increasing from 750 to 1000 because in some computers the initial layout was not applying correctly
*/
private var _applyFirstLayoutTimer:Timer = new Timer(750,1);
private var _applyFirstLayoutTimer:Timer = new Timer(1000,1);
private function handleGetCurrentLayoutResponse(message:Object):void {
_applyFirstLayoutTimer.addEventListener(TimerEvent.TIMER, function(e:TimerEvent):void {
@ -59,12 +61,15 @@ package org.bigbluebutton.modules.layout.services
}
private function onReceivedFirstLayout(message:Object):void {
LOGGER.debug("LayoutService: handling the first layout. locked = [{0}] layout = [{1}]", [message.locked, message.layout]);
lockLayout(message.locked, message.setById);
_dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT));
LOGGER.debug("LayoutService: handling the first layout. locked = [{0}] layout = [{1}]", [message.locked, message.layout]);
trace("LayoutService: handling the first layout. locked = [" + message.locked + "] layout = [" + message.layout + "], moderator = [" + UsersUtil.amIModerator() + "]");
if(message.layout == "" || UsersUtil.amIModerator())
_dispatcher.dispatchEvent(new LayoutEvent(LayoutEvent.APPLY_DEFAULT_LAYOUT_EVENT));
else {
lockLayout(message.locked, message.setById);
handleSyncLayout(message);
}
_dispatcher.dispatchEvent(new ModuleLoadEvent(ModuleLoadEvent.LAYOUT_MODULE_STARTED));
}

View File

@ -132,7 +132,7 @@
emojiBtn.enabled = true;
} else if (data.hasEmojiStatus) {
emojiImg.source = images["emoji_" + data.emojiStatus];
emojiImg.toolTip = ResourceUtil.getInstance().getString('bbb.users.usersGrid.statusItemRenderer.' + data.emojiStatus) + " - " + data.emojiStatusTime.hours + ":" + data.emojiStatusTime.minutes + ":" + data.emojiStatusTime.seconds;
emojiImg.toolTip = ResourceUtil.getInstance().getString('bbb.users.emojiStatus.' + data.emojiStatus) + " - " + data.emojiStatusTime.hours + ":" + data.emojiStatusTime.minutes + ":" + data.emojiStatusTime.seconds;
emojiImg.visible = true;
emojiBtn.visible = false;
emojiBtn.enabled = false;

View File

@ -50,6 +50,7 @@
# 2014-03-10 GUG Enable Webrtc
# 2015-03-12 FFD Added start/stop of HTML5 server
# 2016-01-13 FFD Updates for 1.0
# 2016-02-28 FFD Updates to support HTTPS configuration
#set -x
#set -e
@ -511,13 +512,13 @@ while [ $# -gt 0 ]; do
continue
fi
if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" ]; then
if [ "$1" = "--salt" -o "$1" = "-salt" -o "$1" = "--setsalt" -o "$1" = "--secret" -o "$1" = "-secret" -o "$1" = "--setsecret" ]; then
SALT="${2}"
if [ -z "$SALT" ]; then
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
BBB_WEB_URL=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')
SALT=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
echo
echo " URL: http://$BBB_WEB/bigbluebutton/"
echo " URL: $BBB_WEB_URL/bigbluebutton/"
echo " Salt: $SALT"
echo
exit 0
@ -526,21 +527,6 @@ while [ $# -gt 0 ]; do
continue
fi
if [ "$1" = "--secret" -o "$1" = "-secret" -o "$1" = "--setsecret" ]; then
SALT="${2}"
if [ -z "$SALT" ]; then
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
SALT=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | grep securitySalt | cut -d= -f2);
echo
echo " URL: http://$BBB_WEB/bigbluebutton/"
echo " Secret: $SALT"
echo
exit 0
fi
shift; shift
continue
fi
if [ "$1" = "--lti" -o "$1" = "-lti" ]; then
if [ -z "$SALT" ]; then
if [ -f ${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties ]; then
@ -767,7 +753,7 @@ check_configuration() {
echo
fi
API_IP=$(cat ${SERVLET_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | sed -n '/String BigBlueButtonURL/{s/.*http:\/\///;s/\/.*//;p}' | tr -d '\015')
API_IP=$(cat ${SERVLET_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | sed -n '/String BigBlueButtonURL/{s/.*http[s]*:\/\///;s/\/.*//;p}' | tr -d '\015')
if [ "$IP" != "$API_IP" ]; then
echo "# Warning: API URL IPs do not match host:"
echo "#"
@ -1213,10 +1199,10 @@ check_state() {
if [ -f ${SERVLET_DIR}/demo/demo1.jsp ]; then
BBB_WEB=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
BBB_WEB_URL=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')
echo "# Warning: The API demos are installed and accessible from:"
echo "#"
echo "# http://$BBB_WEB/"
echo "# $BBB_WEB_URL/demo/demo1.jsp"
echo "#"
echo "# These API demos allow anyone to access your server without authentication"
echo "# to create/manage meetings and recordings. They are for testing purposes only."
@ -1228,9 +1214,10 @@ check_state() {
if [ -f /var/www/bigbluebutton/check/conf/config.xml ]; then
CHECK_HOST=$(cat /var/www/bigbluebutton/check/conf/config.xml | grep "<uri>rtmp" | head -1 | sed 's/.*rtmp[s]*:\/\///g' | sed 's/\/.*//g' | tr -d '\015')
BBB_WEB_URL=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*=//;p}')
echo "# Warning: The client self check is installed and accessible from:"
echo "#"
echo "# http://$CHECK_HOST/check"
echo "# $BBB_WEB_URL/check"
echo "#"
echo
@ -1292,7 +1279,7 @@ if [ $CHECK ]; then
need_root
echo
echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(dpkg -l | grep bbb | sed -n '/[0-9].[0-9].[0-9]-1ubuntu/{s/.*ubuntu//;s/;//;p}' | awk '{ sum+=$1} END {print sum}'))"
echo "BigBlueButton Server $BIGBLUEBUTTON_RELEASE ($(dpkg -l | grep bbb | grep -v bbb-demo | grep -v bbb-lti | sed -n '/[0-9].[0-9].[0-9]-1ubuntu/{s/.*ubuntu//;s/;//;p}' | awk '{ sum+=$1} END {print sum}'))"
echo " Kernel version:" `uname -r`
if [ -e /etc/lsb-release ]; then
@ -1345,10 +1332,10 @@ if [ $CHECK ]; then
echo " bbb-web host: $BBB_WEB_IP"
if [ -f ${SERVLET_DIR}/demo/bbb_api_conf.jsp ]; then
API_IP=$(cat ${SERVLET_DIR}/demo/bbb_api_conf.jsp | grep -v '^//' | sed -n '/String BigBlueButtonURL/{s/.*http:\/\///;s/\/.*//;p}' | tr -d '\015')
BBB_WEB_URL=$(cat ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties | grep -v '#' | sed -n '/^bigbluebutton.web.serverURL/{s/.*\///;p}')
echo
echo "${SERVLET_DIR}/demo/bbb_api_conf.jsp (API demos)"
echo " api url: $API_IP"
echo " url: $BBB_WEB_URL"
fi
if [ -f ${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties ]; then
@ -1539,7 +1526,7 @@ if [ -n "$HOST" ]; then
#
# Update configuration for BigBlueButton client
#
echo "Assigning $HOST for http:// in /var/www/bigbluebutton/client/conf/config.xml"
echo "Assigning $HOST for http[s]:// in /var/www/bigbluebutton/client/conf/config.xml"
sudo sed -i "s/http[s]*:\/\/\([^\"\/]*\)\([\"\/]\)/$PROTOCOL_HTTP:\/\/$HOST\2/g" /var/www/bigbluebutton/client/conf/config.xml
echo "Assigning $HOST for publishURI in /var/www/bigbluebutton/client/conf/config.xml"
@ -1553,7 +1540,7 @@ if [ -n "$HOST" ]; then
sudo sed -i "s/bigbluebutton.web.serverURL=http[s]*:\/\/.*/bigbluebutton.web.serverURL=$PROTOCOL_HTTP:\/\/$HOST/g" \
${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties
# 3 paramenter: the file, the variable name, the new value
# 3 paramenter: the file, the variable name, the new value
# echo "Assigning $HOST for FreeSWITCH Event Socket Layer URL in ${SERVLET_DIR}/bigbluebutton/WEB-INF/classes/bigbluebutton.properties"
# change_var_ip /usr/share/red5/webapps/bigbluebutton/WEB-INF/bigbluebutton.properties esl.host $HOST
@ -1576,8 +1563,11 @@ if [ -n "$HOST" ]; then
if [ -f ${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties ]; then
echo "Assigning $HOST for LTI integration in ${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties"
# We don't wat to guess on http/https as the lti endpoint may be a different BigBlueButton server
sed -i "s/bigbluebuttonURL=http:\/\/.*/bigbluebuttonURL=http:\/\/$HOST\/bigbluebutton/g" \
${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties
sed -i "s/bigbluebuttonURL=https:\/\/.*/bigbluebuttonURL=https:\/\/$HOST\/bigbluebutton/g" \
${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties
sed -i "s/ltiEndPoint=.*/ltiEndPoint=$HOST/g" \
${SERVLET_DIR}/lti/WEB-INF/classes/lti.properties
fi
@ -1591,12 +1581,16 @@ if [ -n "$HOST" ]; then
echo -n "Assigning $HOST for playback of recordings: "
for metadata in $(find /var/bigbluebutton/published -name metadata.xml); do
echo -n "."
# Ensure we update both types of URLs
sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata
sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata
done
for metadata in $(find /var/bigbluebutton/unpublished -name metadata.xml); do
echo -n "."
# Ensure we update both types of URLs
sed -i "/<link>/{s/http:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/http:\/\/$HOST\/playback\/$type\2/g}" $metadata
sed -i "/<link>/{s/https:\/\/\([^\"\/]*\)\/playback\/$type\([^<]\)/https:\/\/$HOST\/playback\/$type\2/g}" $metadata
done
echo

View File

@ -70,6 +70,7 @@ mark_for_rebuild() {
for type in $TYPES; do
rm -rf /var/bigbluebutton/published/$type/$MEETING_ID
rm -rf /var/bigbluebutton/unpublished/$type/$MEETING_ID
rm -rf /var/bigbluebutton/deleted/$type/$MEETING_ID
done
# Restart processing at the 'archived' step
@ -92,6 +93,7 @@ mark_for_republish() {
for type in $TYPES; do
rm -rf /var/bigbluebutton/published/$type/$MEETING_ID
rm -rf /var/bigbluebutton/unpublished/$type/$MEETING_ID
rm -rf /var/bigbluebutton/deleted/$type/$MEETING_ID
done
# Restart processing at the 'processed' step
@ -359,6 +361,7 @@ if [ $DELETE ]; then
for type in $TYPES; do
rm -rf /var/bigbluebutton/published/$type/$MEETING_ID*
rm -rf /var/bigbluebutton/unpublished/$type/$MEETING_ID*
rm -rf /var/bigbluebutton/deleted/$type/$MEETING_ID*
rm -rf /var/bigbluebutton/recording/process/$type/$MEETING_ID*
rm -rf /var/bigbluebutton/recording/publish/$type/$MEETING_ID*
@ -386,6 +389,7 @@ if [ $DELETEALL ]; then
for type in $TYPES; do
rm -rf /var/bigbluebutton/published/$type/*
rm -rf /var/bigbluebutton/unpublished/$type/*
rm -rf /var/bigbluebutton/deleted/$type/*
rm -rf /var/bigbluebutton/recording/process/$type/*
rm -rf /var/bigbluebutton/recording/publish/$type/*

View File

@ -278,6 +278,17 @@ Handlebars.registerHelper("getPollQuestions", () => {
}
});
Template.registerHelper('emojiIcons', function () {
return [
{ name: 'sad', icon: 'sad-face', title: '' },
{ name: 'happy', icon: 'happy-face', title: ''},
{ name: 'confused', icon: 'confused-face', title: ''},
{ name: 'neutral', icon: 'neutral-face', title: ''},
{ name: 'away', icon: 'clock', title: ''},
{ name: 'raiseHand', icon: 'hand', title: 'Lower your Hand'}
];
});
this.getSortedUserList = function(users) {
if((users != null ? users.length : void 0) > 1) {
users.sort((a, b) => {

View File

@ -104,44 +104,52 @@
</template>
<template name="icon">
<svg width="{{size}}" height="{{size}}" viewBox="0 0 50 50">
{{#if equals name "happy-face"}}
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 C 21 33, 29 33, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" />
{{else}}
{{#if equals name "neutral-face"}}
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 l 14 0" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
{{else}}
{{#if equals name "confused-face"}}
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="M18 30 C 20 28, 22 28, 25 30 S 30 32, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
{{else}}
{{#if equals name "sad-face"}}
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 C 21 27, 29 27, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" />
{{else}}
{{#if equals name "clock"}}
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<path d="m25 25 l 0 -8" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
<path d="m25 25 l 5 5" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
{{else}}
{{#if equals name "plus"}}
<path d="m25 18 l 0 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
<path d="m18 25 l 14 0" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
{{/if}}
{{/if}}
{{/if}}
{{/if}}
{{/if}}
{{/if}}
</svg>
{{#if equals name "hand"}}
<span rel="tooltip" data-placement="bottom" title="{{title}}">
<i class="ion-android-hand"></i>
</span>
{{else}}
<svg width="{{size}}" height="{{size}}" viewBox="0 0 50 50">
{{> Template.dynamic template=name}}
</svg>
{{/if}}
</template>
<template name="happy-face">
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 C 21 33, 29 33, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" />
</template>
<template name="neutral-face">
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 l 14 0" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
</template>
<template name="confused-face">
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="M18 30 C 20 28, 22 28, 25 30 S 30 32, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
</template>
<template name="sad-face">
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<circle cx="19" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<circle cx="31" cy="22" r="1" stroke="white" stroke-width="2" fill="white"/>
<path d="m18 30 C 21 27, 29 27, 32 30" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" />
</template>
<template name="clock">
<circle cx="25" cy="25" r="14" stroke="white" stroke-width="3" fill="transparent"/>
<path d="m25 25 l 0 -8" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
<path d="m25 25 l 5 5" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
</template>
<template name="plus">
<path d="m25 18 l 0 14" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
<path d="m18 25 l 14 0" stroke="white" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" />
</template>

View File

@ -73,43 +73,24 @@
</span>
{{/unless}}
{{/if}}
{{#if equals user.emoji_status "happy"}}
{{> icon name="happy-face" size="25"}}
{{#if user.presenter}}
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter">
<i class="icon fi-projection-screen statusIcon"></i>
</span>
{{else}}
{{#if equals user.emoji_status "neutral"}}
{{> icon name="neutral-face" size="25"}}
{{#if equals user.role "MODERATOR"}}
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is a moderator">
<i class="icon fi-torso statusIcon"></i>
</span>
{{else}}
{{#if equals user.emoji_status "confused"}}
{{> icon name="confused-face" size="25"}}
{{else}}
{{#if equals user.emoji_status "sad"}}
{{> icon name="sad-face" size="25"}}
{{else}}
{{#if equals user.emoji_status "away"}}
{{> icon name="clock" size="25"}}
{{else}}
{{#if equals user.emoji_status "raiseHand"}}
<span rel="tooltip" data-placement="bottom" title="Lower your hand">
<i class="icon ion-android-hand statusIcon"></i>
</span>
{{else}}
{{#if user.presenter}}
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter">
<i class="icon fi-projection-screen statusIcon"></i>
</span>
{{else}}
{{#if equals user.role "MODERATOR"}}
<span rel="tooltip" data-placement="bottom" title="{{user.name}} is a moderator">
<i class="icon fi-torso statusIcon"></i>
</span>
{{/if}}
{{/if}}
{{/if}}
{{/if}}
{{#each emojiIcons}}
{{#if equals name ../user.emoji_status}}
{{> icon name=icon size="25" title=title}}
{{/if}}
{{/if}}
{{/each}}
{{/if}}
{{/if}}
</div>
{{#if isCurrentUser userId}}

View File

@ -15,24 +15,11 @@
<button type="submit" class="FABTriggerButton">
<i class="ion-android-hand"></i>
</button>
<button type="submit" class="sadEmojiButton {{#if equals getCurrentUserEmojiStatus 'sad'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name="sad-face" size="50"}}
</button>
<button type="submit" class="happyEmojiButton {{#if equals getCurrentUserEmojiStatus 'happy'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name="happy-face" size="50"}}
</button>
<button type="submit" class="confusedEmojiButton {{#if equals getCurrentUserEmojiStatus 'confused'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name="confused-face" size="50"}}
</button>
<button type="submit" class="neutralEmojiButton {{#if equals getCurrentUserEmojiStatus 'neutral'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name="neutral-face" size="50"}}
</button>
<button type="submit" class="awayEmojiButton {{#if equals getCurrentUserEmojiStatus 'away'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name="clock" size="50"}}
</button>
<button type="submit" class="raiseHandEmojiButton {{#if equals getCurrentUserEmojiStatus 'raiseHand'}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
<i class="ion-android-hand"></i>
</button>
{{#each emojiIcons}}
<button type="submit" value={{name}} class="{{name}}EmojiButton {{#if equals getCurrentUserEmojiStatus name}}activeEmojiButton{{else}}inactiveEmojiButton{{/if}}">
{{> icon name=icon size="50"}}
</button>
{{/each}}
</div>
{{#if isMobile}}
{{> makeButton btn_class="soaringButton fullscreenButton whiteboardFullscreenButton" i_class="ion-arrow-expand"}}

View File

@ -53,79 +53,14 @@ Template.whiteboard.events({
return document.webkitExitFullscreen();
}
},
'click .sadEmojiButton.inactiveEmojiButton'(event) {
if($('.sadEmojiButton').css('opacity') === '1') {
'click .inactiveEmojiButton'(event) {
if($(event.target).css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"sad"
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();
}
},
'click .happyEmojiButton.inactiveEmojiButton'(event) {
if($('.happyEmojiButton').css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"happy"
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();
}
},
'click .confusedEmojiButton.inactiveEmojiButton'(event) {
if($('.confusedEmojiButton').css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"confused"
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();
}
},
'click .neutralEmojiButton.inactiveEmojiButton'(event) {
if($('.neutralEmojiButton').css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"neutral"
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();
}
},
'click .awayEmojiButton.inactiveEmojiButton'(event) {
if($('.awayEmojiButton').css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"away"
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();
}
},
'click .raiseHandEmojiButton.inactiveEmojiButton'(event) {
if($('.raiseHandEmojiButton').css('opacity') === '1') {
BBB.setEmojiStatus(
BBB.getMeetingId(),
getInSession('userId'),
getInSession('userId'),
getInSession('authToken'),
"raiseHand"
this.name
);
$('.FABTriggerButton').blur();
return toggleEmojisFAB();

View File

@ -93,11 +93,6 @@ Meteor.methods({
};
Meteor.log.info(`publishing a user mute request for ${toMuteUserId}`);
publish(Meteor.config.redis.channels.toBBBApps.users, message);
updateVoiceUser(meetingId, {
'web_userid': toMuteUserId,
talking: false,
muted: true
});
}
},
@ -130,11 +125,6 @@ Meteor.methods({
};
Meteor.log.info(`publishing a user unmute request for ${toMuteUserId}`);
publish(Meteor.config.redis.channels.toBBBApps.users, message);
updateVoiceUser(meetingId, {
'web_userid': toMuteUserId,
talking: false,
muted: false
});
}
},
userSetEmoji(meetingId, toRaiseUserId, raisedByUserId, raisedByToken, status) {
@ -243,9 +233,9 @@ this.markUserOffline = function(meetingId, userId, callback) {
}, {
$set: {
'user.connection_status': 'offline',
'voiceUser.talking': false,
'voiceUser.joined': false,
'voiceUser.muted': false,
'user.voiceUser.talking': false,
'user.voiceUser.joined': false,
'user.voiceUser.muted': false,
'user.time_of_joining': 0,
'user.listenOnly': false
}

View File

@ -615,8 +615,7 @@ Meteor.startup(() => {
return callback();
} else { // keep moving in the queue
if(indexOf.call(notLoggedEventTypes, eventName) < 0) {
Meteor.log.info(`WARNING!!! THE JSON MESSAGE WAS NOT OF TYPE SUPPORTED BY THIS APPLICATION
${eventName} \n {JSON.stringify(message)}`);
Meteor.log.info(`WARNING!!! THE JSON MESSAGE WAS NOT OF TYPE SUPPORTED BY THIS APPLICATION: ${eventName} \n${JSON.stringify(message)}`);
}
return callback();
}

View File

@ -22,6 +22,7 @@ package org.bigbluebutton.api;
import groovy.util.XmlSlurper;
import groovy.util.slurpersupport.GPathResult;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import org.bigbluebutton.api.domain.Recording;
@ -85,13 +86,22 @@ public class RecordingServiceHelperImp implements RecordingServiceHelper {
xmlEventFile.write writer.toString()
}
public Recording getRecordingInfo(File dir) {
if (dir.isDirectory()) {
def recording = new XmlSlurper().parse(new File(dir.getPath() + File.separatorChar + "metadata.xml"));
return getInfo(recording);
}
return null;
}
public Recording getRecordingInfo(File dir) {
if (dir.isDirectory()) {
try {
File file = new File(dir.getPath() + File.separatorChar + "metadata.xml");
if ( file ) {
def recording = new XmlSlurper().parse(file);
return getInfo(recording);
}
} catch ( FileNotFoundException e) {
// Do nothing, just return null
} catch ( Exception e) {
log.debug(e.getMessage())
}
}
return null;
}
private Recording getInfo(GPathResult rec) {
Recording r = new Recording();

View File

@ -20,14 +20,12 @@
package org.bigbluebutton.api;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -202,7 +200,7 @@ public class RecordingService {
private void createDirectory(File directory) {
if (!directory.exists())
directory.mkdir();
directory.mkdirs();
}
private void deleteDirectory(File directory) {
@ -231,6 +229,7 @@ public class RecordingService {
Path next = iter.next();
files.add(next.toFile());
}
stream.close();
} catch (IOException e) {
e.printStackTrace();
}
@ -326,7 +325,7 @@ public class RecordingService {
return;
}
if (!dest.exists())
dest.mkdir();
dest.mkdirs();
boolean moved = recordings.get(f).renameTo(new File(dest, recordings.get(f).getName()));
if (moved) {
log.debug("Recording successfully moved!");

View File

@ -64,7 +64,7 @@ public class Recording {
public String getState() {
String state = this.state;
if ( state.equals("available") ) {
if ( state == null || state.equals("") || state.equals("available") ) {
state = isPublished()? STATE_PUBLISHED: STATE_UNPUBLISHED;
}
return state;

View File

@ -8,12 +8,12 @@
<#list recs as r>
<recording>
<recordID>${r.getId()}</recordID>
<meetingID>${r.getMeetingID()?html}</meetingID>
<name><![CDATA[${r.getName()}]]></name>
<meetingID><#if r.getMeetingID()?? && r.getMeetingID() != "">${r.getMeetingID()?html}</#if></meetingID>
<name><#if r.getName()?? && r.getName() != ""><![CDATA[${r.getName()}]]></#if></name>
<published>${r.isPublished()?string}</published>
<state>${r.getState()?string}</state>
<startTime>${r.getStartTime()}</startTime>
<endTime>${r.getEndTime()}</endTime>
<startTime><#if r.getStartTime()?? && r.getStartTime() != "">${r.getStartTime()}</#if></startTime>
<endTime><#if r.getEndTime()?? && r.getEndTime() != "">${r.getEndTime()}</#if></endTime>
<#assign m = r.getMetadata()>
<metadata>
<#list m?keys as prop>
@ -21,14 +21,18 @@
</#list>
</metadata>
<playback>
<#if r.getPlaybacks()??>
<#list r.getPlaybacks() as p>
<#if p?? && p.getFormat()??>
<format>
<type>${p.getFormat()}</type>
<url>${p.getUrl()}</url>
<length>${p.getLength()}</length>
<#-- Missing p.getExtensions() -->
</format>
</#if>
</#list>
</#if>
</playback>
</recording>
</#list>

View File

@ -1,25 +0,0 @@
Legacy library to integrate php web applications with BigBlueButton 0.71a
***Getting Started***
You must modify the url and salt in bbb_api_conf.php to that of your bigbluebutton server.
The url must have "/bigbluebutton/" appended to the end. Therefore if the server is at "http://example.com" then url must be "http://example.com/bigbluebutton/".
***Demo***
Demo 1. Join A Course
Demo 2. Join A Selected Course
Demo 3. Join A Selected Course (Password Required)
Demo 4. Activity Monitor
Demo 5. Create Your Own Meeting
***Bugs***
Demo 4. Activity Monitor - It has not been updated since the last release and does not work.
Demo 5. Create Your Own Meeting - If a user is waiting for the moderator to start the meeting they will not be redirected even when the meeting has begun.

View File

@ -1,587 +0,0 @@
<?php
/*
Copyright 2010 Blindside Networks
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas and Sebastian Schneider
(email : omar DOT shammas [a t ] g m ail DOT com)
(email : seb DOT sschneider [ a t ] g m ail DOT com)
1.2 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT com)
*/
function bbb_wrap_simplexml_load_file($url){
if (extension_loaded('curl')) {
$ch = curl_init() or die ( curl_error() );
$timeout = 10;
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
$data = curl_exec( $ch );
curl_close( $ch );
if($data)
return (new SimpleXMLElement($data));
else
return false;
}
return (simplexml_load_file($url));
}
/*
@param
$userName = userName AND meetingID (string)
$welcomeString = welcome message (string)
$modPW = moderator password (string)
$vPW = viewer password (string)
$voiceBridge = voice bridge (integer)
$logout = logout url (url)
*/
// create a meeting and return the url to join as moderator
// TODO::
// create some set methods
class BigBlueButton {
var $userName = array();
var $meetingID; // the meeting id
var $welcomeString;
// the next 2 fields are maybe not needed?!?
var $modPW; // the moderator password
var $attPW; // the attendee pw
var $securitySalt; // the security salt; gets encrypted with sha1
var $URL; // the url the bigbluebutton server is installed
var $sessionURL; // the url for the administrator to join the sessoin
var $userURL;
var $conferenceIsRunning = false;
// this constructor is used to create a BigBlueButton Object
// use this object to create servers
// Use is either 0 arguments or all 7 arguments
public function __construct() {
$numargs = func_num_args();
if( $numargs == 0 ) {
# echo "Constructor created";
}
// pass the information to the class variables
else if( $numargs >= 6 ) {
$this->userName = func_get_arg(0);
$this->meetingID = func_get_arg(1);
$this->welcomeString = func_get_arg(2);
$this->modPW = func_get_arg(3);
$this->attPW = func_get_arg(4);
$this->securitySalt = func_get_arg(5);
$this->URL = func_get_arg(6);
$arg_list = func_get_args();
#debug output for the number of arguments
# for ($i = 0; $i < $numargs; $i++) {
# echo "Argument $i is: " . $arg_list[$i] . "<br />\n";
# }
// $this->createMeeting( $this->userName, $this->meetingID, $this->welcomeString, $this->modPW, $this->attPW, $this->securitySalt, $this->URL );
// echo "Object created";
// echo '<br />';
}// end else if
}
//------------------------------------------------GET URLs-------------------------------------------------
/**
*This method returns the url to join the specified meeting.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param username -- the display name to be used when the user joins the meeting
*@param PW -- the attendee or moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url to join the meeting
*/
public function joinURL( $meetingID, $userName, $PW, $SALT, $URL ) {
$url_join = $URL."api/join?";
$params = 'meetingID='.urlencode($meetingID).'&fullName='.urlencode($userName).'&password='.urlencode($PW);
return ($url_join.$params.'&checksum='.sha1("join".$params.$SALT) );
}
/**
*This method returns the url to join the specified meeting.
*
*@param name -- a name fot the meeting
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param attendeePW -- the attendee of the meeting
*@param moderatorPW -- the moderator of the meeting
*@param welcome -- the welcome message that gets displayed on the chat window
*@param logoutURL -- the URL that the bbb client will go to after users logouut
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url to join the meeting
*/
public function createMeetingURL($name, $meetingID, $attendeePW, $moderatorPW, $welcome, $logoutURL, $SALT, $URL ) {
$url_create = $URL."api/create?";
$voiceBridge = 70000 + rand(0, 9999);
$params = 'name='.urlencode($name).'&meetingID='.urlencode($meetingID).'&attendeePW='.urlencode($attendeePW).'&moderatorPW='.urlencode($moderatorPW).'&voiceBridge='.$voiceBridge.'&logoutURL='.urlencode($logoutURL);
if( trim( $welcome ) )
$params .= '&welcome='.urlencode($welcome);
return ( $url_create.$params.'&checksum='.sha1("create".$params.$SALT) );
}
/**
*This method returns the url to check if the specified meeting is running.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url to check if the specified meeting is running.
*/
public function isMeetingRunningURL( $meetingID, $URL, $SALT ) {
$base_url = $URL."api/isMeetingRunning?";
$params = 'meetingID='.urlencode($meetingID);
return ($base_url.$params.'&checksum='.sha1("isMeetingRunning".$params.$SALT) );
}
/**
*This method returns the url to getMeetingInfo of the specified meeting.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url to check if the specified meeting is running.
*/
public function getMeetingInfoURL( $meetingID, $modPW, $URL, $SALT ) {
$base_url = $URL."api/getMeetingInfo?";
$params = 'meetingID='.urlencode($meetingID).'&password='.urlencode($modPW);
return ( $base_url.$params.'&checksum='.sha1("getMeetingInfo".$params.$SALT));
}
/**
*This method returns the url for listing all meetings in the bigbluebutton server.
*
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url of getMeetings.
*/
public function getMeetingsURL($URL, $SALT) {
$base_url = $URL."api/getMeetings?";
$params = 'random='.(rand() * 1000 );
return ( $base_url.$params.'&checksum='.sha1("getMeetings".$params.$SALT));
}
/**
*This method returns the url to end the specified meeting.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return The url to end the specified meeting.
*/
public function endMeetingURL( $meetingID, $modPW, $URL, $SALT ) {
$base_url = $URL."api/end?";
$params = 'meetingID='.urlencode($meetingID).'&password='.urlencode($modPW);
return ( $base_url.$params.'&checksum='.sha1("end".$params.$SALT) );
}
//-----------------------------------------------CREATE----------------------------------------------------
/**
*This method creates a meeting and returnS the join url for moderators.
*
*@param username
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param welcomeString -- the welcome message to be displayed when a user logs in to the meeting
*@param mPW -- the moderator password of the meeting
*@param aPW -- the attendee password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*@param logoutURL -- the url the user should be redirected to when they logout of bigbluebutton
*
*@return The joinURL if successful or an error message if unsuccessful
*/
public function createMeetingAndGetJoinURL( $username, $meetingID, $welcomeString, $mPW, $aPW, $SALT, $URL, $logoutURL ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::createMeetingURL($username, $meetingID, $aPW, $mPW, $welcomeString, $logoutURL, $SALT, $URL ) );
if( $xml && $xml->returncode == 'SUCCESS' ) {
return ( BigBlueButton::joinURL( $meetingID, $username, $mPW, $SALT, $URL ) );
}
else if( $xml ) {
return ( $xml->messageKey.' : '.$xml->message );
}
else {
return ('Unable to fetch URL '.$url_create.$params.'&checksum='.sha1("create".$params.$SALT) );
}
}
/**
*This method creates a meeting and return an array of the xml packet
*
*@param username
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param welcomeString -- the welcome message to be displayed when a user logs in to the meeting
*@param mPW -- the moderator password of the meeting
*@param aPW -- the attendee password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*@param logoutURL -- the url the user should be redirected to when they logout of bigbluebutton
*
*@return
* - Null if unable to reach the bigbluebutton server
* - If failed it returns an array containing a returncode, messageKey, message.
* - If success it returns an array containing a returncode, messageKey, message, meetingID, attendeePW, moderatorPW, hasBeenForciblyEnded.
*/
public function createMeetingArray( $username, $meetingID, $welcomeString, $mPW, $aPW, $SALT, $URL, $logoutURL ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::createMeetingURL($username, $meetingID, $aPW, $mPW, $welcomeString, $logoutURL, $SALT, $URL ) );
if( $xml ) {
if($xml->meetingID) return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey, 'meetingID' => $xml->meetingID, 'attendeePW' => $xml->attendeePW, 'moderatorPW' => $xml->moderatorPW, 'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded );
else return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey );
}
else {
return null;
}
}
//-------------------------------------------getMeetingInfo---------------------------------------------------
/**
*This method calls the getMeetingInfo on the bigbluebutton server and returns an xml packet.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return An xml packet.
* If failed it returns an xml packet containing a returncode, messagekey, and message.
* If success it returnsan xml packet containing a returncode,
*/
public function getMeetingInfo( $meetingID, $modPW, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingInfoURL( $meetingID, $modPW, $URL, $SALT ) );
if($xml){
return ( str_replace('</response>', '', str_replace("<?xml version=\"1.0\"?>\n<response>", '', $xml->asXML())));
}
return false;
}
/**
*This method calls the getMeetingInfo on the bigbluebutton server and returns an array.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return An Array.
* - Null if unable to reach the bigbluebutton server
* - If failed it returns an array containing a returncode, messagekey, message.
* - If success it returns an array containing a meetingID, moderatorPW, attendeePW, hasBeenForciblyEnded, running, startTime, endTime,
participantCount, moderatorCount, attendees.
*/
public function getMeetingInfoArray( $meetingID, $modPW, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingInfoURL( $meetingID, $modPW, $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' && $xml->messageKey == null){//The meetings were returned
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey );
}
else if($xml && $xml->returncode == 'SUCCESS'){ //If there were meetings already created
return array( 'meetingID' => $xml->meetingID, 'moderatorPW' => $xml->moderatorPW, 'attendeePW' => $xml->attendeePW, 'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded, 'running' => $xml->running, 'startTime' => $xml->startTime, 'endTime' => $xml->endTime, 'participantCount' => $xml->participantCount, 'moderatorCount' => $xml->moderatorCount, 'attendees' => $xml->attendees );
}
else if( ($xml && $xml->returncode == 'FAILED') || $xml) { //If the xml packet returned failure it displays the message to the user
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else { //If the server is unreachable, then prompts the user of the necessary action
return null;
}
}
//-----------------------------------------------getMeetings------------------------------------------------------
/**
*This method calls getMeetings on the bigbluebutton server, then calls getMeetingInfo for each meeting and concatenates the result.
*
*@param URL -- the url of the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*
*@return
* - If failed then returns a boolean of false.
* - If succeeded then returns an xml of all the meetings.
*/
public function getMeetings( $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingsURL( $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' ) {
if( $xml->messageKey )
return ( $xml->message->asXML() );
ob_start();
echo '<meetings>';
if( count( $xml->meetings ) && count( $xml->meetings->meeting ) ) {
foreach ($xml->meetings->meeting as $meeting)
{
echo '<meeting>';
echo BigBlueButton::getMeetingInfo($meeting->meetingID, $meeting->moderatorPW, $URL, $SALT);
echo '</meeting>';
}
}
echo '</meetings>';
return (ob_get_clean());
}
else {
return (false);
}
}
/**
*This method calls getMeetings on the bigbluebutton server, then calls getMeetingInfo for each meeting and concatenates the result.
*
*@param URL -- the url of the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*
*@return
* - Null if the server is unreachable
* - If FAILED then returns an array containing a returncode, messageKey, message.
* - If SUCCESS then returns an array of all the meetings. Each element in the array is an array containing a meetingID,
moderatorPW, attendeePW, hasBeenForciblyEnded, running.
*/
public function getMeetingsArray( $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingsURL( $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' && $xml->messageKey ) {//The meetings were returned
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else if($xml && $xml->returncode == 'SUCCESS'){ //If there were meetings already created
foreach ($xml->meetings->meeting as $meeting)
{
//$meetings[] = BigBlueButton::getMeetingInfo($meeting->meetingID, $meeting->moderatorPW, $URL, $SALT);
$meetings[] = array( 'meetingID' => $meeting->meetingID, 'moderatorPW' => $meeting->moderatorPW, 'attendeePW' => $meeting->attendeePW, 'hasBeenForciblyEnded' => $meeting->hasBeenForciblyEnded, 'running' => $meeting->running );
}
return $meetings;
}
else if( $xml ) { //If the xml packet returned failure it displays the message to the user
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else { //If the server is unreachable, then prompts the user of the necessary action
return null;
}
}
//----------------------------------------------getUsers---------------------------------------
/**
*This method prints the usernames of the attendees in the specified conference.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param URL -- the url of the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*@param UNAME -- is a boolean to determine how the username is formatted when printed. Default if false.
*
*@return A boolean of true if the attendees were printed successfully and false otherwise.
*/
public function getUsers( $meetingID, $modPW, $URL, $SALT, $UNAME = false ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingInfoURL( $meetingID, $modPW, $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' ) {
ob_start();
if( count( $xml->attendees ) && count( $xml->attendees->attendee ) ) {
foreach ( $xml->attendees->attendee as $attendee ) {
if( $UNAME == true ) {
echo "User name: ".$attendee->fullName.'<br />';
}
else {
echo $attendee->fullName.'<br />';
}
}
}
return (ob_end_flush());
}
else {
return (false);
}
}
/**
*This method returns an array of the attendees in the specified meeting.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param URL -- the url of the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*
*@return
* - Null if the server is unreachable.
* - If FAILED, returns an array containing a returncode, messageKey, message.
* - If SUCCESS, returns an array of array containing the userID, fullName, role of each attendee
*/
public function getUsersArray( $meetingID, $modPW, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::getMeetingInfoURL( $meetingID, $modPW, $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' && $xml->messageKey == null ) {//The meetings were returned
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else if($xml && $xml->returncode == 'SUCCESS'){ //If there were meetings already created
foreach ($xml->attendees->attendee as $attendee){
$users[] = array( 'userID' => $attendee->userID, 'fullName' => $attendee->fullName, 'role' => $attendee->role );
}
return $users;
}
else if( $xml ) { //If the xml packet returned failure it displays the message to the user
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else { //If the server is unreachable, then prompts the user of the necessary action
return null;
}
}
//------------------------------------------------Other Methods------------------------------------
/**
*This method calls end meeting on the specified meeting in the bigbluebutton server.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param modPW -- the moderator password of the meeting
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return
* - Null if the server is unreachable
* - An array containing a returncode, messageKey, message.
*/
public function endMeeting( $meetingID, $modPW, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::endMeetingURL( $meetingID, $modPW, $URL, $SALT ) );
if( $xml ) { //If the xml packet returned failure it displays the message to the user
return array('returncode' => $xml->returncode, 'message' => $xml->message, 'messageKey' => $xml->messageKey);
}
else { //If the server is unreachable, then prompts the user of the necessary action
return null;
}
}
/**
*This method check the BigBlueButton server to see if the meeting is running (i.e. there is someone in the meeting)
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return A boolean of true if the meeting is running and false if it is not running
*/
public function isMeetingRunning( $meetingID, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::isMeetingRunningURL( $meetingID, $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS' )
return ( ( $xml->running == 'true' ) ? true : false);
else
return ( false );
}
/**
*This method calls isMeetingRunning on the BigBlueButton server.
*
*@param meetingID -- the unique meeting identifier used to store the meeting in the bigbluebutton server
*@param SALT -- the security salt of the bigbluebutton server
*@param URL -- the url of the bigbluebutton server
*
*@return
* - If SUCCESS it returns an xml packet
* - If the FAILED or the server is unreachable returns a string of 'false'
*/
public function getMeetingXML( $meetingID, $URL, $SALT ) {
$xml = bbb_wrap_simplexml_load_file( BigBlueButton::isMeetingRunningURL( $meetingID, $URL, $SALT ) );
if( $xml && $xml->returncode == 'SUCCESS')
return ( str_replace('</response>', '', str_replace("<?xml version=\"1.0\"?>\n<response>", '', $xml->asXML())));
else
return 'false';
}
// TODO: WRITE AN ITERATOR WHICH GOES OVER WHATEVER IT IS BEING TOLD IN THE API AND LIST INFORMATION
/* we have to define at least 2 variable fields for getInformation to read out information at any position
The first is: An identifier to chose if we look for attendees or the meetings or something else
The second is: An identifier to chose what integrated functions are supposed to be used
@param IDENTIFIER -- needs to be put in for the function to identify the information to print out
current values which can be used are 'attendee' and 'meetings'
@param meetingID -- needs to be put in to identify the meeting
@param modPW -- needs to be put in if the users are supposed to be shown or to retrieve information about the meetings
@param URL -- needs to be put in the URL to the bigbluebutton server
@param SALT -- needs to be put in for the security salt calculation
Note: If 'meetings' is used, then only the parameters URL and SALT needs to be used
If 'attendee' is used, then all the parameters needs to be used
*/
public function getInformation( $IDENTIFIER, $meetingID, $modPW, $URL, $SALT ) {
// if the identifier is null or '', then return false
if( $IDENTIFIER == "" || $IDENTIFIER == null ) {
echo "You need to type in a valid value into the identifier.";
return false;
}
// if the identifier is attendee, call getUsers
else if( $IDENTIFIER == 'attendee' ) {
return BigBlueButton::getUsers( $meetingID, $modPW, $URL, $SALT );
}
// if the identifier is meetings, call getMeetings
else if( $IDENTIFIER == 'meetings' ) {
return BigBlueButton::getMeetings( $URL, $SALT );
}
// return nothing
else {
return true;
}
}
function getServerIP() {
// get the server url
$sIP = $_SERVER['SERVER_ADDR'];
return $serverIP = 'http://'.$sIP.'/bigbluebutton/';
}
}
?>

View File

@ -1,9 +0,0 @@
<?php
// This is the security salt that must match the value set in the BigBlueButton server
$salt = "1234567890abcdefghijklmnopqrstuvwxyz";
// This is the URL for the BigBlueButton server
//Make sure the url ends with /bigbluebutton/
$url = "http://example.com/bigbluebutton/";
?>

View File

@ -1,17 +0,0 @@
<?php
//================================================================================
//------------------Required Libraries and Global Variables-----------------------
//================================================================================
require('bbb_api.php');
require('bbb_api_conf.php');
//================================================================================
//------------------------------------Main----------------------------------------
//================================================================================
echo '<?xml version="1.0"?>'."\r\n";
header('content-type: text/xml');
//Calls getMeetingXML and returns returns the result
echo BigBlueButton::getMeetingXML( $_GET['meetingID'], $url, $salt );
?>

View File

@ -1,98 +0,0 @@
<?php
/*
Copyright 2010 BigBlueButton
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT .com)
*/
require('bbb_api.php');
require('bbb_api_conf.php'); //enter the salt, and url in this file
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Join a Course</title>
</head>
<body>
<br />
<?php
if ($_REQUEST['action'] == 'Join' && trim($_REQUEST['username'])){
//Calls endMeeting on the bigbluebutton server
$response = BigBlueButton::createMeetingArray($_REQUEST['username'], "Demo Meeting", null, "mp", "ap", $salt, $url, "http://bigbluebutton.org");
//Analyzes the bigbluebutton server's response
if(!$response){//If the server is unreachable
$msg = 'Unable to join the meeting. Please check the url of the bigbluebutton server AND check to see if the bigbluebutton server is running.';
}
else if( $response['returncode'] == 'FAILED' ) { //The meeting was not created
if($response['messageKey'] == 'checksumError'){
$msg = 'A checksum error occured. Make sure you entered the correct salt.';
}
else{
$msg = $response['message'];
}
}
else{ //The meeting was created, and the user will now be joined
$bbb_joinURL = BigBlueButton::joinURL("Demo Meeting", $_REQUEST['username'],"mp", $salt, $url);
?><script type="text/javascript"> window.location = "<?php echo $bbb_joinURL; ?>";</script><?php
return;
}
}
else if($_REQUEST['action'] == 'Join'){
$msg = "You must enter your name.";
}
include('demo_header.php');
?>
<h2>Demo #1: Join a Course</h2>
<?php
if($msg) echo '<p style="color:red;"><strong>'.$msg.'</strong></p>';
?>
<form name="form1" method="get">
<table cellspacing="7" cellpadding="7">
<tr>
<td>
Enter your name:
</td>
<td>
<input type="text" name="username" />
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" name="action" value="Join" />
</td>
</tr>
</table>
</form>
<?php
include('demo_footer.php');
?>
</body>
</html>

View File

@ -1,110 +0,0 @@
<?php
/*
Copyright 2010 BigBlueButton
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT .com)
*/
require('bbb_api.php');
require('bbb_api_conf.php'); //enter the salt, and url in this file
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Join a Selected Course</title>
</head>
<body>
<br />
<?php
if ($_REQUEST['action'] == 'Join' && trim($_REQUEST['username']) && trim($_REQUEST['meetingID'])){
$meetingID = trim($_REQUEST['meetingID']);
$username = trim($_REQUEST['username']);
$response = BigBlueButton::createMeetingArray($username, $meetingID, null, "mp", "ap", $salt, $url, "http://bigbluebutton.org");
//Analyzes the bigbluebutton server's response
if(!$response){//If the server is unreachable
$msg = 'Unable to join the meeting. Please check the url of the bigbluebutton server AND check to see if the bigbluebutton server is running.';
}
else if( $response['returncode'] == 'FAILED' ) { //The meeting was not created
if($response['messageKey'] == 'checksumError'){
$msg = 'A checksum error occured. Make sure you entered the correct salt.';
}
else{
$msg = $response['message'];
}
}
else{ //The meeting was created, and the user will now be joined
$bbb_joinURL = BigBlueButton::joinURL($meetingID, $username,$response['moderatorPW'], $salt, $url);
?><script type="text/javascript"> window.location = "<?php echo $bbb_joinURL; ?>";</script><?php
return;
}
}
else if($_REQUEST['action'] == 'Join'){
$msg = "All fields need to be filled";
}
include('demo_header.php');
?>
<h2>Demo #2: Join a Selected Course</h2>
<?php
if($msg) echo '<p style="color:red;"><strong>'.$msg.'</strong></p>';
?>
<form name="form1" method="get">
<table cellspacing="7" cellpadding="7">
<tr>
<td>
Enter your name:
</td>
<td>
<input type="text" name="username" />
</td>
</tr>
<tr>
<td>
Select a Course:
</td>
<td>
<select name="meetingID">
<option value="English 232">English 232</option>
<option value="English 300">English 300</option>
<option value="English 402">English 402</option>
<option value="Demo Meeting">Demo Meeting</option>
</select>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" name="action" value="Join" />
</td>
</tr>
</table>
</form>
<?php
include('demo_footer.php');
?>
</body>
</html>

View File

@ -1,145 +0,0 @@
<?php
/*
Copyright 2010 BigBlueButton
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT .com)
*/
require('bbb_api.php');
require('bbb_api_conf.php'); //enter the salt, and url in this file
$meetings['ENGL-2013'] = array('meetingID' => 'ENGL-2013', 'display' => 'ENGL-2013: Research Methods in English', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['ENGL-2213'] = array('meetingID' => 'ENGL-2213', 'display' => 'ENGL-2213: Drama Production I', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['ENGL-2023'] = array('meetingID' => 'ENGL-2023', 'display' => 'ENGL-2023: Survey of English Literature', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['LAW-1323'] = array('meetingID' => 'LAW-1323', 'display' => 'LAW-1323: Fundamentals of Advocacy', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['LAW-2273'] = array('meetingID' => 'LAW-2273', 'display' => 'LAW-2273: Business Organizations', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['LAW-3113'] = array('meetingID' => 'LAW-3113', 'display' => 'LAW-3113: Corporate Finance', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['VOH-SteveStoyan'] = array('meetingID' => 'VOH-SteveStoyan', 'display' => 'Virtual Office Hours - Steve Stoyan', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['VOH-MikeSmith'] = array('meetingID' => 'VOH-MikeSmith', 'display' => 'Virtual Office Hours - Mike Smith', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
$meetings['VOH-TonyRomo'] = array('meetingID' => 'VOH-TonyRomo', 'display' => 'Virtual Office Hours - Tony Romo', 'moderatorPW' => "prof123", 'attendeePW' => 'student123');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Join a Selected Course</title>
</head>
<body>
<br />
<?php
if ( $_REQUEST['action'] == 'Join' && trim($_REQUEST['username']) && trim($_REQUEST['meetingID']) && trim($_REQUEST['password']) ){
$meetingID = trim($_REQUEST['meetingID']);
$username = trim($_REQUEST['username']);
$password = trim($_REQUEST['password']);
$meeting = $meetings[$meetingID];
if($password == $meeting['moderatorPW'] || $password == $meeting['attendeePW']){
$response = BigBlueButton::createMeetingArray($_REQUEST['username'], $meetingID, null, $meeting['moderatorPW'], $meeting['attendeePW'], $salt, $url, "http://bigbluebutton.org");
//Analyzes the bigbluebutton server's response
if(!$response){//If the server is unreachable
$msg = 'Unable to join the meeting. Please check the url of the bigbluebutton server AND check to see if the bigbluebutton server is running.';
}
else if( $response['returncode'] == 'FAILED' ) { //The meeting was not created
if($response['messageKey'] == 'checksumError'){
$msg = 'A checksum error occured. Make sure you entered the correct salt.';
}
else{
$msg = $response['message'];
}
}
else{ //The meeting was created, and the user will now be joined
$bbb_joinURL = BigBlueButton::joinURL($meetingID, $_REQUEST['username'],$password, $salt, $url);
?><script type="text/javascript"> window.location = "<?php echo $bbb_joinURL; ?>";</script><?php
return;
}
}
else{
$msg = 'Incorrect Password';
}
}
else if($_REQUEST['action'] == 'Join'){
$msg = "All fields need to be filled";
}
include('demo_header.php');
?>
<h2>Demo #3: Join a Selected Course (Password Required)</h2>
<?php
if($msg) echo '<p style="color:red;"><strong>'.$msg.'</strong></p>';
?>
<form name="form1" method="get">
<table cellspacing="7" cellpadding="7">
<tr>
<td>
Enter your name:
</td>
<td>
<input type="text" name="username" />
</td>
</tr>
<tr>
<td>
Select a Course:
</td>
<td>
<select name="meetingID">
<?php
foreach($meetings as $meeting){
echo "<option value='".$meeting['meetingID']."'>".$meeting['display']."</option>";
}
?>
</select>
</td>
</tr>
<tr>
<td>
Password:
</td>
<td>
<input type="text" name="password" />
</td>
</tr>
<tr>
<td />
<td>
<input type="submit" name="action" value="Join" />
</td>
</tr>
</table>
</form>
Passwords:
<ul>
<li>
prof123 - login as a professor (moderator privileges)
</li>
<li>
student123 - login as a student (attendee privileges)
</li>
</ul>
<?php
include('demo_footer.php');
?>
</body>
</html>

View File

@ -1,147 +0,0 @@
/*
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
BigBlueButton 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 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, If not, see <http://www.gnu.org/licenses/>.
Author: Islam El-Ashi <ielashi@gmail.com>
*/
var meetings; // will hold the meetings data
$(document).ready(function(){
updateMeetingInfo(); // update the available meeting information as soon as the page is loaded.
setInterval("updateMeetingInfo()", 15000); // update the meeting information every 15 seconds
});
// For each active meeting, create a table listing the participants and insert it into the page.
function createMeetings() {
var nOfMeetings = 0;
if (meetings.meeting) {
for (var i in meetings.meeting) {
// this variable is to work around the JSON nuance of having a variable instead of an array of one member.
// if we detect there is more than one meeting use the array, otherwise use the variable
var meeting = (meetings.meeting[i].attendees != null) ? meetings.meeting[i] : meetings.meeting;
createMeetingTable(meeting);
if (meeting.participantCount != "0") nOfMeetings++;
// this is also to work around the JSON nuance, if we previously detected that there is only
// one element (i.e. a variable) then we shouldn't loop again.
if (meeting == meetings.meeting) break;
}
}
// if there are no meetings then display a message to the user.
if (nOfMeetings == 0) {
$("#no_meetings").text("No meetings currently running.");
$("#meetings").text('');
}
else
$("#no_meetings").text('');
}
// creates div tags for each meeting to be able to insert the meeting table into the DOM afterwards
function initializeDivTags() {
if (meetings.meeting) {
var meetingID;
var meeting;
var encodedMeetingID;
for (var i in meetings.meeting) {
meeting = (meetings.meeting[i].attendees != null) ? meetings.meeting[i] : meetings.meeting;
meetingID = meeting.meetingID;
encodedMeeting = encode(meetingID);
if ($("#" + encodedMeeting).length == 0) {
// assign the div tag an id unique to each meeting
// the id assigned is the encoded value of the meeting id, the encoding is to avoid
// special characters and spaces in the id
$("#meetings").append('<div id="' + encodedMeeting + '" class="hiddenDiv"></div>');
}
if (meeting == meetings.meeting) break;
}
}
}
// call the demo4 helper page to fetch the updated data, this is executed every 15 seconds.
function updateMeetingInfo() {
$.ajax({
type: "GET",
url: 'demo4_helper.php?getxml=true',
dataType: "text/xml",
cache: false,
success: function(xml) {
meetings = $.xml2json(xml);
initializeDivTags();
createMeetings();
},
error: function() {
$("#no_meetings").text("Failed to connect to API.");
$("#meetings").text("");
}
});
}
function getMeetingsInfoURL(meetingID, password, checksum) {
return '../api/getMeetingInfo?meetingID=' + meetingID + '&password=' + password + '&checksum=' + checksum;
}
function createMeetingTable(meeting) {
var tableContent = '<table name="' + meeting.meetingID + '" class="hor-minimalist-b" cellspacing="0" summary="The current participants in a meeting"><caption>' + meeting.meetingID + '</caption><tr><th scope="col" abbr="Participants">Participants</th><th scope="col" abbr="Name">Name</th><th scope="col" abbr="Role">Role</th></tr>';
var encodedMeetingID = encode(meeting.meetingID);
var tableRowId;
var newRows = new Array();
var numberOfRows = 0;
if (meeting.attendees.attendee) {
for (var i in meeting.attendees.attendee) {
var attendee = (meeting.attendees.attendee[i].userID != null) ? meeting.attendees.attendee[i] : meeting.attendees.attendee;
tableRowId = encodedMeetingID + '_' + attendee.userID;
tableContent += '<tr id="' + tableRowId + '"><td>' + attendee.userID + '<td>' + attendee.fullName + '</td><td>' + attendee.role + '</td></tr>'
// if there is a new row to be added, then add to the new rows array to display it with a flash effect.
if ($("#" + tableRowId).length == 0) {
newRows[newRows.length] = tableRowId;
}
numberOfRows++;
if (attendee == meeting.attendees.attendee) break;
}
}
tableContent += '</table>';
if (numberOfRows > 0) {
$("#" + encodedMeetingID).html(tableContent);
$("#" + encodedMeetingID).show("fast");
}
else {
$("#" + encodedMeetingID).hide("fast");
}
for (var i = 0; i < newRows.length; i++) {
$("#" + newRows[i]).effect("highlight", {}, 3000);
}
}
// the encoding hashes the string to an md5, which ensures - to a great extent - that the encoded string will be
// 1. unique per the original string
// 2. has no spaces and/or special characters
function encode(string) {
return hex_md5(string);
}

View File

@ -1,54 +0,0 @@
<?
/*
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
BigBlueButton 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 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, If not, see <http://www.gnu.org/licenses/>.
Author: DJP <DJP@architectes.org>
*/
require('bbb_api.inc.php');
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<title>Get Meeting Info</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="demo4.js"></script>
<script type="text/javascript" src="md5.js"></script>
<script type="text/javascript" src="jquery.xml2json.js"></script>
<style type="text/css">
.hiddenDiv {display:none;}
.hor-minimalist-b{font-family:"Lucida Sans Unicode", "Lucida Grande", Sans-Serif;font-size:12px;background:#fff;width:480px;border-collapse:collapse;text-align:left;margin:20px;}.hor-minimalist-b th{font-size:14px;font-weight:normal;color:#039;border-bottom:2px solid #6678b1;padding:10px 8px;}.hor-minimalist-b td{border-bottom:1px solid #ccc;color:#669;padding:6px 8px;width:100px;}.hor-minimalist-b tbody tr:hover td{color:#009;}</style>
</head>
<body>
<br />
<?
include('demo_header.php');
?>
<h2>Demo #4: Activity Monitor</h2>
<p id="no_meetings"></p>
<div id="meetings"></div>
<?
include('demo_footer.php');
?>
</body>
</html>

View File

@ -1,28 +0,0 @@
<?
/*
BigBlueButton - http://www.bigbluebutton.org
Copyright (c) 2008-2009 by respective authors (see below). All rights reserved.
BigBlueButton 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 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, If not, see <http://www.gnu.org/licenses/>.
Author: DJP <DJP@architectes.org>
*/
require('bbb_api.inc.php');
echo '<?xml version="1.0"?>'."\r\n";
header('content-type: text/xml');
echo getMeetings();
?>

View File

@ -1,297 +0,0 @@
<?php
/*
Copyright 2010 BigBlueButton
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT .com)
*/
require('bbb_api.php');
require('bbb_api_conf.php'); //enter the salt, and url in this file
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />
<title>Create Your Own Meeting</title>
<script type="text/javascript"
src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript" src="heartbeat.js"></script>
</head>
<body>
<br />
<?php
$step = 1;
switch ($_REQUEST['action'])
{
case 'create':
if (trim($_REQUEST['username']))
{
/*
* This is the URL for to join the meeting as moderator
*/
$meetingID = $_REQUEST['username']."'s meeting";
//Calls endMeeting on the bigbluebutton server
$response = BigBlueButton::createMeetingArray($_REQUEST['username'], $meetingID, null, "mp", "ap", $salt, $url, "http://bigbluebutton.org");
//Analyzes the bigbluebutton server's response
if(!$response){//If the server is unreachable
echo '<div class="updated"><p><strong>Unable to join the meeting. Please check the url of the bigbluebutton server AND check to see if the bigbluebutton server is running.</strong></p></div>';
//return;
}
else if( $response['returncode'] == 'FAILED' ) { //The meeting was not created
if($response['messageKey'] == 'checksumError'){
echo '<div class="updated"><p><strong>A checksum error occured. Make sure you entered the correct salt.</strong></p></div>';
//return;
}
else{
echo '<div class="updated"><p><strong>'.$response['message'].'</strong></p></div>';
//return;
}
}
else{ //The meeting was created, and the user will now be joined
$bbb_joinURL = BigBlueButton::joinURL($meetingID, $_REQUEST['username'],"mp", $salt, $url);
}
/*
* We're going to extract the meetingToken to enable others to join as viewers
*/
$inviteURL = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?action=invite&meetingID='.urlencode($meetingID);
$step = 2;
}
break;
case 'invite':
/*
* We have an invite to an active meeting. Ask the person for their name so they can join.
*/
if (trim($_REQUEST['meetingID']))
{
$step = 3;
}
break;
case 'enter':
/*
* The user is now attempting to join the meeting
*/
if (trim($_REQUEST['username'])&& trim($_REQUEST['meetingID'])){
$bbb_joinURL = BigBlueButton::joinURL($_REQUEST['meetingID'], $_REQUEST['username'],"ap", $salt, $url);
//$joinURL = 'http://'.$_SERVER['HTTP_HOST'].$_SERVER['SCRIPT_NAME'].'?action=join&username='.urlencode($_REQUEST['username']).'&meetingToken='.urlencode($_REQUEST['meetingToken']);
if (BigBlueButton::isMeetingRunning( $_REQUEST['meetingID'], $url, $salt ))
{
?>
<script language="javascript" type="text/javascript">
window.location.href="<?php echo $bbb_joinURL;?>";
</script>
<?php
}
else
{
/*
* The meeting has not yet started, so check until we get back the status that the meeting is running
*/
$step = 4;
$checkMeetingStatus = BigBlueButton::getMeetingInfoURL( $_REQUEST['meetingID'], 'mp', $url, $salt );
}
}
else if (!$_REQUEST['username']){
$msg = "You must enter your name.";
$step = 3;
}
break;
case 'isMeetingRunning':
/*
* This function proxy the request "isMeetingRunning" through PHP Script to BBB Server so we don't have any AJAX security issue
*/
ob_clean();
$checkMeetingStatus = BigBlueButton::isMeetingRunningURL( $_REQUEST['meetingID'], $url, $salt );
echo file_get_contents($checkMeetingStatus);
die;
break;
case 'join':
/*
* We have an invite request to join an existing meeting and the meeting is running
* We don't need to pass a meeting description as it's already been set by the first time the meeting was created.
*/
$bbb_joinURL = BigBlueButton::joinURL($_REQUEST['meetingID'], $_REQUEST['username'],"ap", $salt, $url);
?>
<script language="javascript" type="text/javascript">window.location.href="<?php echo $bbb_joinURL;?>";</script>
<?php
break;
default:
break;
}
switch ($step)
{
case '1':
include('demo_header.php');
?>
<h2>Demo #5: Create Your Own Meeting</h2>
<form name="form1" method="get">
<table cellspacing="7" cellpadding="7">
<tr>
<td><strong>Step 1</strong></td>
</tr>
<tr>
<td>
Enter your name:
</td>
<td>
<input type="text" name="username" />
<input type="hidden" name="action" value="create"/>
</td>
</tr>
<tr>
<td></td>
<td>
<input id="submit-button" type="submit" value="Create meeting" />
</td>
</tr>
</table>
</form>
<script>
//
// We could have asked the user for both their name and a meeting title, but we'll just use their name to create a title
// We'll use JQuery to dynamically update the button
//
$(document).ready(function(){
$("input[name='username']").keyup(function() {
if ($("input[name='username']").val() == "") {
$("#submit-button").attr('value',"Create meeting" );
} else {
$("#submit-button").attr('value',"Create " +$("input[name='username']").val()+ "'s meeting" );
}
});
});
</script>
<?php
break;
case '2':
include('demo_header.php');
?>
<h2><strong><?php echo $meetingID; ?></strong> has been created.</h2>
<p>
<strong>Step 2. </strong> Invite others using the following link: <a href="<?php echo $inviteURL;?>"><?php echo $inviteURL;?></a>
</p>
<p><strong>Step 3. </strong> Click the following link to start your meeting:
<a href="<?php echo $bbb_joinURL; ?>">Start Meeting</a>
</p>
<?php
break;
case '3':
?>
<hr />
<h2>Invite</h2>
<?php
if($msg) echo '<p style="color:red;"><strong>'.$msg.'</strong></p>';
?>
<form name="form3" method="get">
<table cellspacing="7" cellpadding="7">
<tr>
<td colspan=2>You have been invited to join <strong><?php echo $_REQUEST['meetingID'];?></strong>.</td>
</tr>
<tr>
<td width="50%">
Enter your name:
</td>
<td>
<input type="text" name="username" /><br />
<input type="hidden" name="meetingID" value="<?php echo $_REQUEST['meetingID'];?>" />
<input type="hidden" name="action" value="enter" />
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Join" />
</td>
<tr>
</table>
</form>
<?php
break;
case '4':
//$checkMeetingStatus = BigBlueButton::getUrlOfRunningMeeting($_REQUEST['meetingID'], $url, $salt );
?>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
<script type="text/javascript" src="<?php echo 'heartbeat.js'; ?>"></script>
<script type="text/javascript" src="<?php echo 'md5.js'; ?>"></script>
<script type="text/javascript" src="<?php echo 'jquery.xml2json.js'; ?>"></script>
<script type="text/javascript">
$(document).ready(function(){
$.jheartbeat.set({
url: 'check.php?meetingID=<?php echo urlencode(trim($_REQUEST['meetingID'])); ?>'
delay: 5000
}, function () {
mycallback();
});
});
function mycallback() {
// Not elegant, but works around a bug in IE8
var isMeetingRunning = ($("#HeartBeatDIV").text().search("true") > 0 );
if (isMeetingRunning) {
window.location = "<?php echo $bbb_joinURL; ?>";
}
}
</script>
<hr />
<h2><strong><?php echo $_REQUEST['meetingID'];?></strong> has not yet started.</h2>
<table>
<tbody>
<tr>
<td>
<p>Hi <?php echo $_REQUEST['username'];?>,</p>
<p>Now waiting for the moderator to start the meeting.</p>
<br />
<img align="center" src="<?php echo 'polling.gif'; ?>" />
<br />
<p>(Your browser will automatically refresh and join the meeting when it starts.)</p>
</td>
</tr>
</tbody>
</table>
<?php
break;
}
include('demo_footer.php');
?>
</body>
</html>

View File

@ -1,3 +0,0 @@
<hr />
<p/>
<small>These demos use the <a href="http://code.google.com/p/bigbluebutton/wiki/API">BigBlueButton API</a>. The source code for these demos is available <a href="http://code.google.com/p/bigbluebutton/source/browse/#svn/trunk/bbb-api-examples/PHP/">here</a>.</small>

View File

@ -1,2 +0,0 @@
<a href="demo1.php">Join a Course</a> | <a href="demo2.php">Join a Selected Course</a> | <a href="demo3.php">Join a Selected Course (Password Required)</a> | <a href="demo5.php">Create Your Own Meeting</a>
<hr />

View File

@ -1,43 +0,0 @@
/*
* JHeartbeat 0.1.1 Beta
* By Jason Levine (http://www.jasons-toolbox.com)
* A heartbeat plugin for the jquery library to help keep sessions alive.
*/
$.jheartbeat = {
options: {
url: "heartbeat_default.asp",
delay: 10000
},
beatfunction: function(){
},
timeoutobj: {
id: -1
},
set: function(options, onbeatfunction) {
if (this.timeoutobj.id > -1) {
clearTimeout(this.timeoutobj);
}
if (options) {
$.extend(this.options, options);
}
if (onbeatfunction) {
this.beatfunction = onbeatfunction;
}
// Add the HeartBeatDIV to the page
$("body").append("<div id=\"HeartBeatDIV\" style=\"display: none;\"></div>");
this.timeoutobj.id = setTimeout("$.jheartbeat.beat();", this.options.delay);
},
beat: function() {
$("#HeartBeatDIV").load(this.options.url);
this.timeoutobj.id = setTimeout("$.jheartbeat.beat();", this.options.delay);
this.beatfunction();
}
};

View File

@ -1,154 +0,0 @@
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);

View File

@ -1,160 +0,0 @@
/*
### jQuery XML to JSON Plugin v1.0 - 2008-07-01 ###
* http://www.fyneworks.com/ - diego@fyneworks.com
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
###
Website: http://www.fyneworks.com/jquery/xml-to-json/
*//*
# INSPIRED BY: http://www.terracoder.com/
AND: http://www.thomasfrank.se/xml_to_json.html
AND: http://www.kawa.net/works/js/xml/objtree-e.html
*//*
This simple script converts XML (document of code) into a JSON object. It is the combination of 2
'xml to json' great parsers (see below) which allows for both 'simple' and 'extended' parsing modes.
*/
// Avoid collisions
;if(window.jQuery) (function($){
// Add function to jQuery namespace
$.extend({
// converts xml documents and xml text to json object
xml2json: function(xml, extended) {
if(!xml) return {}; // quick fail
//### PARSER LIBRARY
// Core function
function parseXML(node, simple){
if(!node) return null;
var txt = '', obj = null, att = null;
var nt = node.nodeType, nn = jsVar(node.localName || node.nodeName);
var nv = node.text || node.nodeValue || '';
/*DBG*/ //if(window.console) console.log(['x2j',nn,nt,nv.length+' bytes']);
if(node.childNodes){
if(node.childNodes.length>0){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'CHILDREN',node.childNodes]);
$.each(node.childNodes, function(n,cn){
var cnt = cn.nodeType, cnn = jsVar(cn.localName || cn.nodeName);
var cnv = cn.text || cn.nodeValue || '';
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>a',cnn,cnt,cnv]);
if(cnt == 8){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>b',cnn,'COMMENT (ignore)']);
return; // ignore comment node
}
else if(cnt == 3 || cnt == 4 || !cnn){
// ignore white-space in between tags
if(cnv.match(/^\s+$/)){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>c',cnn,'WHITE-SPACE (ignore)']);
return;
};
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>d',cnn,'TEXT']);
txt += cnv.replace(/^\s+/,'').replace(/\s+$/,'');
// make sure we ditch trailing spaces from markup
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>e',cnn,'OBJECT']);
obj = obj || {};
if(obj[cnn]){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>f',cnn,'ARRAY']);
if(!obj[cnn].length) obj[cnn] = myArr(obj[cnn]);
obj[cnn][ obj[cnn].length ] = parseXML(cn, true/* simple */);
obj[cnn].length = obj[cnn].length;
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'node>g',cnn,'dig deeper...']);
obj[cnn] = parseXML(cn);
};
};
});
};//node.childNodes.length>0
};//node.childNodes
if(node.attributes){
if(node.attributes.length>0){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'ATTRIBUTES',node.attributes])
att = {}; obj = obj || {};
$.each(node.attributes, function(a,at){
var atn = jsVar(at.name), atv = at.value;
att[atn] = atv;
if(obj[atn]){
/*DBG*/ //if(window.console) console.log(['x2j',nn,'attr>',atn,'ARRAY']);
if(!obj[atn].length) obj[atn] = myArr(obj[atn]);//[ obj[ atn ] ];
obj[atn][ obj[atn].length ] = atv;
obj[atn].length = obj[atn].length;
}
else{
/*DBG*/ //if(window.console) console.log(['x2j',nn,'attr>',atn,'TEXT']);
obj[atn] = atv;
};
});
//obj['attributes'] = att;
};//node.attributes.length>0
};//node.attributes
if(obj){
obj = $.extend( (txt!='' ? new String(txt) : {}),/* {text:txt},*/ obj || {}/*, att || {}*/);
txt = (obj.text) ? (typeof(obj.text)=='object' ? obj.text : [obj.text || '']).concat([txt]) : txt;
if(txt) obj.text = txt;
txt = '';
};
var out = obj || txt;
//console.log([extended, simple, out]);
if(extended){
if(txt) out = {};//new String(out);
txt = out.text || txt || '';
if(txt) out.text = txt;
if(!simple) out = myArr(out);
};
return out;
};// parseXML
// Core Function End
// Utility functions
var jsVar = function(s){ return String(s || '').replace(/-/g,"_"); };
var isNum = function(s){ return (typeof s == "number") || String((s && typeof s == "string") ? s : '').test(/^((-)?([0-9]*)((\.{0,1})([0-9]+))?$)/); };
var myArr = function(o){
if(!o.length) o = [ o ]; o.length=o.length;
// here is where you can attach additional functionality, such as searching and sorting...
return o;
};
// Utility functions End
//### PARSER LIBRARY END
// Convert plain text to xml
if(typeof xml=='string') xml = $.text2xml(xml);
// Quick fail if not xml (or if this is a node)
if(!xml.nodeType) return;
if(xml.nodeType == 3 || xml.nodeType == 4) return xml.nodeValue;
// Find xml root node
var root = (xml.nodeType == 9) ? xml.documentElement : xml;
// Convert xml to json
var out = parseXML(root, true /* simple */);
// Clean-up memory
xml = null; root = null;
// Send output
return out;
},
// Convert text to XML DOM
text2xml: function(str) {
// NOTE: I'd like to use jQuery for this, but jQuery makes all tags uppercase
//return $(xml)[0];
var out;
try{
var xml = ($.browser.msie)?new ActiveXObject("Microsoft.XMLDOM"):new DOMParser();
xml.async = false;
}catch(e){ throw new Error("XML Parser could not be instantiated") };
try{
if($.browser.msie) out = (xml.loadXML(str))?xml:false;
else out = xml.parseFromString(str, "text/xml");
}catch(e){ throw new Error("Error parsing XML string") };
return out;
}
}); // extend $
})(jQuery);

View File

@ -1,379 +0,0 @@
/*
* A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
* Digest Algorithm, as defined in RFC 1321.
* Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for more info.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
{ return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
{ return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
{ return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
/*
* Perform a simple self-test to see if the VM is working
*/
function md5_vm_test()
{
return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}
/*
* Calculate the MD5 of a raw string
*/
function rstr_md5(s)
{
return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}
/*
* Calculate the HMAC-MD5, of a key and some data (raw strings)
*/
function rstr_hmac_md5(key, data)
{
var bkey = rstr2binl(key);
if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input)
{
try { hexcase } catch(e) { hexcase=0; }
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var output = "";
var x;
for(var i = 0; i < input.length; i++)
{
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F)
+ hex_tab.charAt( x & 0x0F);
}
return output;
}
/*
* Convert a raw string to a base-64 string
*/
function rstr2b64(input)
{
try { b64pad } catch(e) { b64pad=''; }
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var output = "";
var len = input.length;
for(var i = 0; i < len; i += 3)
{
var triplet = (input.charCodeAt(i) << 16)
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
}
}
return output;
}
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any(input, encoding)
{
var divisor = encoding.length;
var i, j, q, x, quotient;
/* Convert to an array of 16-bit big-endian values, forming the dividend */
var dividend = Array(Math.ceil(input.length / 2));
for(i = 0; i < dividend.length; i++)
{
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. All remainders are stored for later
* use.
*/
var full_length = Math.ceil(input.length * 8 /
(Math.log(encoding.length) / Math.log(2)));
var remainders = Array(full_length);
for(j = 0; j < full_length; j++)
{
quotient = Array();
x = 0;
for(i = 0; i < dividend.length; i++)
{
x = (x << 16) + dividend[i];
q = Math.floor(x / divisor);
x -= q * divisor;
if(quotient.length > 0 || q > 0)
quotient[quotient.length] = q;
}
remainders[j] = x;
dividend = quotient;
}
/* Convert the remainders to the output string */
var output = "";
for(i = remainders.length - 1; i >= 0; i--)
output += encoding.charAt(remainders[i]);
return output;
}
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
function str2rstr_utf8(input)
{
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
/* Decode utf-16 surrogate pairs */
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
/* Encode output as utf-8 */
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}
/*
* Encode a string as utf-16
*/
function str2rstr_utf16le(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF);
return output;
}
function str2rstr_utf16be(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
input.charCodeAt(i) & 0xFF);
return output;
}
/*
* Convert a raw string to an array of little-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binl(input)
{
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
return output;
}
/*
* Convert an array of little-endian words to a string
*/
function binl2rstr(input)
{
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
return output;
}
/*
* Calculate the MD5 of an array of little-endian words, and a bit length.
*/
function binl_md5(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << ((len) % 32);
x[(((len + 64) >>> 9) << 4) + 14] = len;
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
}
return Array(a, b, c, d);
}
/*
* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn(q, a, b, x, s, t)
{
return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@ -1,8 +1 @@
Library to integrate php web applications with BigBlueButton 0.8
Contributed by:
Peter Mentzer.
Original version available at:
https://github.com/petermentzer/bbb-api-php
Stick the entire /bbb-api-php directory somewhere that you can host php web files. Then browse to the index page for configuration and usage instructions.
See [https://github.com/bigbluebutton/bigbluebutton-api-php](https://github.com/bigbluebutton/bigbluebutton-api-php).

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -1,18 +0,0 @@
body {font-family:Arial,Helvetica,sans-serif;}
dl dt {font-weight:bold;margin-bottom:10px;}
dl dd {margin-bottom:20px;}
p {color:#666;}
ul {margin-left:30px;padding-left:0;list-style-type:none;}
li {margin-bottom:5px;}
a {color:#3678c1;}
h3 {border-bottom:1px solid #666;padding-bottom:5px;margin-top:30px;}
#main {width:960px;margin: 0 auto;}
#status {background-color:#fff;border: solid 1px #ccc;padding:20px;text-align:center;}
#TOC {float:right;margin-left:20px;margin-bottom:20px;padding:0px;padding-left:0;border:1px solid #ccc;width:300px;}
#tocheader {background-color:#666;color:white;padding:10px;font-weight:bold;}
.method {padding-left:20px;background-color:#d7ffd9;border:1px solid #97e59b;margin-top:0px;}
.methodname {font-weight:bold;color:#000;}
.example {padding-left:40px;padding-right:40px; background-color:#fff;border:1px solid #ccc;}
.code {background-color:#eee;padding:20px;border:1px solid #ccc;}

View File

@ -1,61 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ CREATE MEETING w/ OPTIONS ______ */
/*
*/
$creationParams = array(
'meetingId' => '1234', // REQUIRED
'meetingName' => 'Test Meeting Name', // REQUIRED
'attendeePw' => 'ap', // Match this value in getJoinMeetingURL() to join as attendee.
'moderatorPw' => 'mp', // Match this value in getJoinMeetingURL() to join as moderator.
'welcomeMsg' => '', // ''= use default. Change to customize.
'dialNumber' => '', // The main number to call into. Optional.
'voiceBridge' => '12345', // 5 digit PIN to join voice conference. Required.
'webVoice' => '', // Alphanumeric to join voice. Optional.
'logoutUrl' => '', // Default in bigbluebutton.properties. Optional.
'maxParticipants' => '-1', // Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => 'false', // New. 'true' will tell BBB to record the meeting.
'duration' => '0', // Default = 0 which means no set duration in minutes. [number]
//'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
);
// Create the meeting and get back a response:
$itsAllGood = true;
try {$result = $bbb->createMeetingWithXmlResponseArray($creationParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
print_r($result);
if ($result['returncode'] == 'SUCCESS') {
// Then do stuff ...
echo "<p>Meeting succesfullly created.</p>";
}
else {
echo "<p>Meeting creation failed.</p>";
}
}
}
?>

View File

@ -1,61 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ CREATE MEETING w/ OPTIONS ______ */
/*
*/
$creationParams = array(
'meetingId' => '12345', // REQUIRED
'meetingName' => 'Test Recorded Meeting Name', // REQUIRED
'attendeePw' => 'ap', // Match this value in getJoinMeetingURL() to join as attendee.
'moderatorPw' => 'mp', // Match this value in getJoinMeetingURL() to join as moderator.
'welcomeMsg' => '', // ''= use default. Change to customize.
'dialNumber' => '', // The main number to call into. Optional.
'voiceBridge' => '12345', // 5 digit PIN to join voice. Required.
'webVoice' => '', // Alphanumeric to join voice. Optional.
'logoutUrl' => '', // Default in bigbluebutton.properties. Optional.
'maxParticipants' => '-1', // Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => 'true', // New. 'true' will tell BBB to record the meeting.
'duration' => '5', // Default = 0 which means no set duration in minutes. [number]
//'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
);
// Create the meeting and get back a response:
$itsAllGood = true;
try {$result = $bbb->createMeetingWithXmlResponseArray($creationParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
print_r($result);
if ($result['returncode'] == 'SUCCESS') {
// Then do stuff ...
echo "<p>Meeting succesfullly created.</p>";
}
else {
echo "<p>Meeting creation failed.</p>";
}
}
}
?>

View File

@ -1,43 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ DELETE RECORDINGS ______ */
/* Pass a recordId to delete a recording.
*/
$recordingParams = array(
/*
* NOTE: Set the recordId below to a valid id after you have created a recorded meeting,
* and received back a real recordID back from your BBB server using the
* getRecordingsWithXmlResponseArray method.
*/
// REQUIRED - We have to know which recording:
'recordId' => '8cb2237d0679ca88db6464eac60da96345513964-1333379469215',
);
// Delete the meeting:
$itsAllGood = true;
try {$result = $bbb->deleteRecordingsWithXmlResponseArray($recordingParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,50 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ END A MEETING ______ */
/* Determine the meeting to end via meetingId and end it.
*/
$endParams = array(
'meetingId' => '1234', // REQUIRED - We have to know which meeting to end.
'password' => 'mp', // REQUIRED - Must match moderator pass for meeting.
);
// Get the URL to end a meeting:
$itsAllGood = true;
try {$result = $bbb->endMeetingWithXmlResponseArray($endParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
print_r($result);
if ($result['returncode'] == 'SUCCESS') {
// Then do stuff ...
echo "<p>Meeting succesfullly ended.</p>";
}
else {
echo "<p>Failed to end meeting.</p>";
}
}
}
?>

View File

@ -1,40 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ JOIN MEETING w/ OPTIONS ______ */
/* Determine the meeting to join via meetingId and join it.
*/
$joinParams = array(
'meetingId' => '1234', // REQUIRED - We have to know which meeting to join.
'username' => 'Test Attendee', // REQUIRED - The user display name that will show in the BBB meeting.
'password' => 'ap', // REQUIRED - Must match either attendee or moderator pass for meeting.
'createTime' => '', // OPTIONAL - string
'userId' => '', // OPTIONAL - string
'webVoiceConf' => '' // OPTIONAL - string
);
// Get the URL to join meeting:
$itsAllGood = true;
try {$result = $bbb->getJoinMeetingURL($joinParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,40 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ JOIN MEETING w/ OPTIONS ______ */
/* Determine the meeting to join via meetingId and join it.
*/
$joinParams = array(
'meetingId' => '1234', // REQUIRED - We have to know which meeting to join.
'username' => 'Test Moderator', // REQUIRED - The user display name that will show in the BBB meeting.
'password' => 'mp', // REQUIRED - Must match either attendee or moderator pass for meeting.
'createTime' => '', // OPTIONAL - string
'userId' => '', // OPTIONAL - string
'webVoiceConf' => '' // OPTIONAL - string
);
// Get the URL to join meeting:
$itsAllGood = true;
try {$result = $bbb->getJoinMeetingURL($joinParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,40 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ JOIN MEETING w/ OPTIONS ______ */
/* Determine the meeting to join via meetingId and join it.
*/
$joinParams = array(
'meetingId' => '12345', // REQUIRED - We have to know which meeting to join.
'username' => 'Test Moderator', // REQUIRED - The user display name that will show in the BBB meeting.
'password' => 'mp', // REQUIRED - Must match either attendee or moderator pass for meeting.
'createTime' => '', // OPTIONAL - string
'userId' => '', // OPTIONAL - string
'webVoiceConf' => '' // OPTIONAL - string
);
// Get the URL to join meeting:
$itsAllGood = true;
try {$result = $bbb->getJoinMeetingURL($joinParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,51 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ GET MEETING INFO ______ */
/* Get meeting info based on meeting id.
*/
$infoParams = array(
'meetingId' => '1234', // REQUIRED - We have to know which meeting.
'password' => 'mp', // REQUIRED - Must match moderator pass for meeting.
);
// Now get meeting info and display it:
$itsAllGood = true;
try {$result = $bbb->getMeetingInfoWithXmlResponseArray($infoParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
var_dump($result);
if (!isset($result['messageKey'])) {
// Then do stuff ...
echo "<p>Meeting info was found on the server.</p>";
}
else {
echo "<p>Failed to get meeting info.</p>";
}
}
}
?>

View File

@ -1,53 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ GET MEETINGS FROM BBB SERVER ______ */
/*
*/
/*
---DEBUG - useful for manually checking the raw xml results.
$test = $bbb->getGetMeetingsUrl();
echo $test;
---END DEBUG
*/
$itsAllGood = true;
try {$result = $bbb->getMeetingsWithXmlResponseArray();}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
if ($result['returncode'] == 'SUCCESS') {
// Then do stuff ...
echo "<p>We got some meeting info from BBB:</p>";
// You can parse this array how you like. For now we just do this:
print_r($result);
}
else {
echo "<p>We didn't get a success response. Instead we got this:</p>";
print_r($result);
}
}
}
?>

View File

@ -1,50 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ GET RECORDINGS INFO ______ */
/* Get recordings info based on optional meeting id, or all.
*/
$recordingsParams = array(
'meetingId' => '', // OPTIONAL - comma separate if multiples
);
// Now get recordings info and display it:
$itsAllGood = true;
try {$result = $bbb->getRecordingsWithXmlResponseArray($recordingsParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
// If it's all good, then we've interfaced with our BBB php api OK:
if ($result == null) {
// If we get a null response, then we're not getting any XML back from BBB.
echo "Failed to get any response. Maybe we can't contact the BBB server.";
}
else {
// We got an XML response, so let's see what it says:
var_dump($result);
if ($result['returncode'] == 'SUCCESS') {
// Then do stuff ...
echo "<p>Meeting info was found on the server.</p>";
}
else {
echo "<p>Failed to get meeting info.</p>";
}
}
}
?>

View File

@ -1,33 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ IS MEETING RUNNING? ______ */
/* Pass a meetingId to see if the meeting is currently running.
*/
$meetingId = '1234';
// Get the URL to join meeting:
$itsAllGood = true;
try {$result = $bbb->isMeetingRunningWithXmlResponseArray($meetingId);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,89 +0,0 @@
<!DOCTYPE html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>joinIfRunning</title>
<link type="text/css" rel="stylesheet" href="../css/main.css">
<script type="text/javascript" charset="utf-8">
if(window.top==window) {
// you're not in a frame so you reload the site
window.setTimeout('location.reload()', 10000); //reloads after 10 seconds
} else {
//you're inside a frame, so you stop reloading
}
</script>
</head>
<body id="joinifrunning" onload="">
<div id="main">
<h1>Attempting to Join...</h1>
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
$bbb = new BigBlueButton();
$meetingId = '1234';
$itsAllGood = true;
try {$result = $bbb->isMeetingRunningWithXmlResponseArray($meetingId);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
//print_r($result);
$status = $result['running'];
//echo "<p style='color:red;'>".$status."</p>";
$holdMessage = '
<div id="status">
<p>Your meeting has not yet started. Waiting for a moderator to start the meeting...</p>
<img src="../assets/ajax-loader.gif" alt="...contacting server..." />
<p>You will be connected as soon as the meeting starts.</p>
</div>
';
// The meeting is not running yet so hold your horses:
if ($status == 'false') {
echo $holdMessage;
}
else {
//Here we redirect the user to the joinUrl.
// For now we output this:
echo "...User would be redirected to the meeting now at:";
$joinParams = array(
'meetingId' => '1234', // REQUIRED - We have to know which meeting to join.
'username' => 'Test Attendee', // REQUIRED - The user display name that will show in the BBB meeting.
'password' => 'ap', // REQUIRED - Must match either attendee or moderator pass for meeting.
'createTime' => '', // OPTIONAL - string
'userId' => '', // OPTIONAL - string
'webVoiceConf' => '' // OPTIONAL - string
);
// Get the URL to join meeting:
$allGood = true;
try {$result = $bbb->getJoinMeetingURL($joinParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$allGood = false;
}
if ($allGood == true) {
//Output resulting URL. Send user there...
echo "<p>".$result."</p>";
}
}
}
?>
</div>
</body>

View File

@ -1,44 +0,0 @@
<?php
/* _____ PHP Big Blue Button API Usage ______
* by Peter Mentzer peter@petermentzerdesign.com
* Use, modify and distribute however you like.
*/
// Require the bbb-api file:
require_once('../includes/bbb-api.php');
// Instatiate the BBB class:
$bbb = new BigBlueButton();
/* ___________ PUBLISH RECORDINGS ______ */
/* Pass a recordId and true/false to publish or unpublish a recording.
*/
$recordingParams = array(
/*
* NOTE: Set the recordId below to a valid id after you have created a recorded meeting,
* and received back a real recordID back from your BBB server using the
* getRecordingsWithXmlResponseArray method.
*/
// REQUIRED - We have to know which recording:
'recordId' => '8cb2237d0679ca88db6464eac60da96345513964-1333379469215',
'publish' => 'true', // REQUIRED - To publish or not to publish.
);
// Now do it:
$itsAllGood = true;
try {$result = $bbb->publishRecordingsWithXmlResponseArray($recordingParams);}
catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
$itsAllGood = false;
}
if ($itsAllGood == true) {
//Output results to see what we're getting:
print_r($result);
}
?>

View File

@ -1,594 +0,0 @@
<?php
/*
BigBlueButton open source conferencing system - http://www.bigbluebutton.org/
Copyright (c) 2012 BigBlueButton Inc. and by respective authors (see below).
This program is free software; you can redistribute it and/or modify it under the
terms of the GNU Lesser General Public License as published by the Free Software
Foundation; either version 3.0 of the License, or (at your option) any later
version.
BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
Versions:
1.0 -- Initial version written by DJP
(email: djp [a t ] architectes DOT .org)
1.1 -- Updated by Omar Shammas and Sebastian Schneider
(email : omar DOT shammas [a t ] g m ail DOT com)
(email : seb DOT sschneider [ a t ] g m ail DOT com)
1.2 -- Updated by Omar Shammas
(email : omar DOT shammas [a t ] g m ail DOT com)
1.3 -- Refactored by Peter Mentzer
(email : peter@petermentzerdesign.com)
- This update will BREAK your external existing code if
you've used the previous versions <= 1.2 already so:
-- update your external code to use new method names if needed
-- update your external code to pass new parameters to methods
- Working example of joinIfRunning.php now included
- Added support for BBB 0.8b recordings
- Now using Zend coding, naming and style conventions
- Refactored methods to accept standardized parameters & match BBB API structure
-- See included samples for usage examples
1.4 -- Updated by xaker1
(email : admin [a t ] xaker1 DOT ru)
*/
/* _______________________________________________________________________*/
/* get the config values */
require_once "config.php";
class BigBlueButton {
private $_securitySalt;
private $_bbbServerBaseUrl;
/* ___________ General Methods for the BigBlueButton Class __________ */
function __construct() {
/*
Establish just our basic elements in the constructor:
*/
// BASE CONFIGS - set these for your BBB server in config.php and they will
// simply flow in here via the constants:
$this->_securitySalt = CONFIG_SECURITY_SALT;
$this->_bbbServerBaseUrl = CONFIG_SERVER_BASE_URL;
}
private function _processXmlResponse($url, $xml = ''){
/*
A private utility method used by other public methods to process XML responses.
*/
if (extension_loaded('curl')) {
$ch = curl_init() or die ( curl_error() );
$timeout = 10;
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt( $ch, CURLOPT_URL, $url );
curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, $timeout);
if(!empty($xml)){
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
curl_setopt($ch, CURLOPT_HTTPHEADER, array(
'Content-type: application/xml',
'Content-length: ' . strlen($xml)
));
}
$data = curl_exec( $ch );
curl_close( $ch );
if($data)
return (new SimpleXMLElement($data));
else
return false;
}
if(!empty($xml))
throw new Exception('Set xml, but curl does not installed.');
return (simplexml_load_file($url));
}
private function _requiredParam($param, $name = '') {
/* Process required params and throw errors if we don't get values */
if ((isset($param)) && ($param != '')) {
return $param;
}
elseif (!isset($param)) {
throw new Exception('Missing parameter.');
}
else {
throw new Exception(''.$name.' is required.');
}
}
private function _optionalParam($param) {
/* Pass most optional params through as set value, or set to '' */
/* Don't know if we'll use this one, but let's build it in case. */
if ((isset($param)) && ($param != '')) {
return $param;
}
else {
$param = '';
return $param;
}
}
/* __________________ BBB ADMINISTRATION METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- create
-- join
-- end
*/
public function getCreateMeetingUrl($creationParams) {
/*
USAGE:
(see $creationParams array in createMeetingArray method.)
*/
$this->_meetingId = $this->_requiredParam($creationParams['meetingId'], 'meetingId');
$this->_meetingName = $this->_requiredParam($creationParams['meetingName'], 'meetingName');
// Set up the basic creation URL:
$creationUrl = $this->_bbbServerBaseUrl."api/create?";
// Add params:
$params =
'name='.urlencode($this->_meetingName).
'&meetingID='.urlencode($this->_meetingId).
'&attendeePW='.urlencode($creationParams['attendeePw']).
'&moderatorPW='.urlencode($creationParams['moderatorPw']).
'&dialNumber='.urlencode($creationParams['dialNumber']).
'&voiceBridge='.urlencode($creationParams['voiceBridge']).
'&webVoice='.urlencode($creationParams['webVoice']).
'&logoutURL='.urlencode($creationParams['logoutUrl']).
'&maxParticipants='.urlencode($creationParams['maxParticipants']).
'&record='.urlencode($creationParams['record']).
'&duration='.urlencode($creationParams['duration']);
//'&meta_category='.urlencode($creationParams['meta_category']);
$welcomeMessage = $creationParams['welcomeMsg'];
if(trim($welcomeMessage))
$params .= '&welcome='.urlencode($welcomeMessage);
// Return the complete URL:
return ( $creationUrl.$params.'&checksum='.sha1("create".$params.$this->_securitySalt) );
}
public function createMeetingWithXmlResponseArray($creationParams, $xml = '') {
/*
USAGE:
$creationParams = array(
'name' => 'Meeting Name', -- A name for the meeting (or username)
'meetingId' => '1234', -- A unique id for the meeting
'attendeePw' => 'ap', -- Set to 'ap' and use 'ap' to join = no user pass required.
'moderatorPw' => 'mp', -- Set to 'mp' and use 'mp' to join = no user pass required.
'welcomeMsg' => '', -- ''= use default. Change to customize.
'dialNumber' => '', -- The main number to call into. Optional.
'voiceBridge' => '12345', -- 5 digit PIN to join voice conference. Required.
'webVoice' => '', -- Alphanumeric to join voice. Optional.
'logoutUrl' => '', -- Default in bigbluebutton.properties. Optional.
'maxParticipants' => '-1', -- Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => 'false', -- New. 'true' will tell BBB to record the meeting.
'duration' => '0', -- Default = 0 which means no set duration in minutes. [number]
'meta_category' => '', -- Use to pass additional info to BBB server. See API docs to enable.
);
$xml = ''; -- Use to pass additional xml to BBB server. Example, use to Preupload Slides. See API docs.
*/
$xml = $this->_processXmlResponse($this->getCreateMeetingURL($creationParams), $xml);
if($xml) {
if($xml->meetingID)
return array(
'returncode' => $xml->returncode,
'message' => $xml->message,
'messageKey' => $xml->messageKey,
'meetingId' => $xml->meetingID,
'attendeePw' => $xml->attendeePW,
'moderatorPw' => $xml->moderatorPW,
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded,
'createTime' => $xml->createTime
);
else
return array(
'returncode' => $xml->returncode,
'message' => $xml->message,
'messageKey' => $xml->messageKey
);
}
else {
return null;
}
}
public function getJoinMeetingURL($joinParams) {
/*
NOTE: At this point, we don't use a corresponding joinMeetingWithXmlResponse here because the API
doesn't respond on success, but you can still code that method if you need it. Or, you can take the URL
that's returned from this method and simply send your users off to that URL in your code.
USAGE:
$joinParams = array(
'meetingId' => '1234', -- REQUIRED - A unique id for the meeting
'username' => 'Jane Doe', -- REQUIRED - The name that will display for the user in the meeting
'password' => 'ap', -- REQUIRED - The attendee or moderator password, depending on what's passed here
'createTime' => '', -- OPTIONAL - string. Leave blank ('') unless you set this correctly.
'userID' => '', -- OPTIONAL - string
'webVoiceConf' => '' -- OPTIONAL - string
);
*/
$this->_meetingId = $this->_requiredParam($joinParams['meetingId'], 'meetingId');
$this->_username = $this->_requiredParam($joinParams['username'], 'username');
$this->_password = $this->_requiredParam($joinParams['password'], 'password');
// Establish the basic join URL:
$joinUrl = $this->_bbbServerBaseUrl."api/join?";
// Add parameters to the URL:
$params =
'meetingID='.urlencode($this->_meetingId).
'&fullName='.urlencode($this->_username).
'&password='.urlencode($this->_password).
'&userID='.urlencode($joinParams['userId']).
'&webVoiceConf='.urlencode($joinParams['webVoiceConf']);
// Only use createTime if we really want to use it. If it's '', then don't pass it:
if (((isset($joinParams['createTime'])) && ($joinParams['createTime'] != ''))) {
$params .= '&createTime='.urlencode($joinParams['createTime']);
}
// Return the URL:
return ($joinUrl.$params.'&checksum='.sha1("join".$params.$this->_securitySalt));
}
public function getEndMeetingURL($endParams) {
/* USAGE:
$endParams = array (
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($endParams['meetingId'], 'meetingId');
$this->_password = $this->_requiredParam($endParams['password'], 'password');
$endUrl = $this->_bbbServerBaseUrl."api/end?";
$params =
'meetingID='.urlencode($this->_meetingId).
'&password='.urlencode($this->_password);
return ($endUrl.$params.'&checksum='.sha1("end".$params.$this->_securitySalt));
}
public function endMeetingWithXmlResponseArray($endParams) {
/* USAGE:
$endParams = array (
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$xml = $this->_processXmlResponse($this->getEndMeetingURL($endParams));
if($xml) {
return array(
'returncode' => $xml->returncode,
'message' => $xml->message,
'messageKey' => $xml->messageKey
);
}
else {
return null;
}
}
/* __________________ BBB MONITORING METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- isMeetingRunning
-- getMeetings
-- getMeetingInfo
*/
public function getIsMeetingRunningUrl($meetingId) {
/* USAGE:
$meetingId = '1234' -- REQUIRED - The unique id for the meeting
*/
$this->_meetingId = $this->_requiredParam($meetingId, 'meetingId');
$runningUrl = $this->_bbbServerBaseUrl."api/isMeetingRunning?";
$params =
'meetingID='.urlencode($this->_meetingId);
return ($runningUrl.$params.'&checksum='.sha1("isMeetingRunning".$params.$this->_securitySalt));
}
public function isMeetingRunningWithXmlResponseArray($meetingId) {
/* USAGE:
$meetingId = '1234' -- REQUIRED - The unique id for the meeting
*/
$xml = $this->_processXmlResponse($this->getIsMeetingRunningUrl($meetingId));
if($xml) {
return array(
'returncode' => $xml->returncode,
'running' => $xml->running // -- Returns true/false.
);
}
else {
return null;
}
}
public function getGetMeetingsUrl() {
/* Simply formulate the getMeetings URL
We do this in a separate function so we have the option to just get this
URL and print it if we want for some reason.
*/
$getMeetingsUrl = $this->_bbbServerBaseUrl."api/getMeetings?checksum=".sha1("getMeetings".$this->_securitySalt);
return $getMeetingsUrl;
}
public function getMeetingsWithXmlResponseArray() {
/* USAGE:
We don't need to pass any parameters with this one, so we just send the query URL off to BBB
and then handle the results that we get in the XML response.
*/
$xml = $this->_processXmlResponse($this->getGetMeetingsUrl());
if($xml) {
// If we don't get a success code, stop processing and return just the returncode:
if ($xml->returncode != 'SUCCESS') {
$result = array(
'returncode' => $xml->returncode
);
return $result;
}
elseif ($xml->messageKey == 'noMeetings') {
/* No meetings on server, so return just this info: */
$result = array(
'returncode' => $xml->returncode,
'messageKey' => $xml->messageKey,
'message' => $xml->message
);
return $result;
}
else {
// In this case, we have success and meetings. First return general response:
$result = array(
'returncode' => $xml->returncode,
'messageKey' => $xml->messageKey,
'message' => $xml->message
);
// Then interate through meeting results and return them as part of the array:
foreach ($xml->meetings->meeting as $m) {
$result[] = array(
'meetingId' => $m->meetingID,
'meetingName' => $m->meetingName,
'createTime' => $m->createTime,
'attendeePw' => $m->attendeePW,
'moderatorPw' => $m->moderatorPW,
'hasBeenForciblyEnded' => $m->hasBeenForciblyEnded,
'running' => $m->running
);
}
return $result;
}
}
else {
return null;
}
}
public function getMeetingInfoUrl($infoParams) {
/* USAGE:
$infoParams = array(
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$this->_meetingId = $this->_requiredParam($infoParams['meetingId'], 'meetingId');
$this->_password = $this->_requiredParam($infoParams['password'], 'password');
$infoUrl = $this->_bbbServerBaseUrl."api/getMeetingInfo?";
$params =
'meetingID='.urlencode($this->_meetingId).
'&password='.urlencode($this->_password);
return ($infoUrl.$params.'&checksum='.sha1("getMeetingInfo".$params.$this->_securitySalt));
}
public function getMeetingInfoWithXmlResponseArray($infoParams) {
/* USAGE:
$infoParams = array(
'meetingId' => '1234', -- REQUIRED - The unique id for the meeting
'password' => 'mp' -- REQUIRED - The moderator password for the meeting
);
*/
$xml = $this->_processXmlResponse($this->getMeetingInfoUrl($infoParams));
if($xml) {
// If we don't get a success code or messageKey, find out why:
if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) {
$result = array(
'returncode' => $xml->returncode,
'messageKey' => $xml->messageKey,
'message' => $xml->message
);
return $result;
}
else {
// In this case, we have success and meeting info:
$result = array(
'returncode' => $xml->returncode,
'meetingName' => $xml->meetingName,
'meetingId' => $xml->meetingID,
'createTime' => $xml->createTime,
'voiceBridge' => $xml->voiceBridge,
'attendeePw' => $xml->attendeePW,
'moderatorPw' => $xml->moderatorPW,
'running' => $xml->running,
'recording' => $xml->recording,
'hasBeenForciblyEnded' => $xml->hasBeenForciblyEnded,
'startTime' => $xml->startTime,
'endTime' => $xml->endTime,
'participantCount' => $xml->participantCount,
'maxUsers' => $xml->maxUsers,
'moderatorCount' => $xml->moderatorCount,
);
// Then interate through attendee results and return them as part of the array:
foreach ($xml->attendees->attendee as $a) {
$result[] = array(
'userId' => $a->userID,
'fullName' => $a->fullName,
'role' => $a->role
);
}
return $result;
}
}
else {
return null;
}
}
/* __________________ BBB RECORDING METHODS _________________ */
/* The methods in the following section support the following categories of the BBB API:
-- getRecordings
-- publishRecordings
-- deleteRecordings
*/
public function getRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/getRecordings?";
$params =
'meetingID='.urlencode($recordingParams['meetingId']);
return ($recordingsUrl.$params.'&checksum='.sha1("getRecordings".$params.$this->_securitySalt));
}
public function getRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'meetingId' => '1234', -- OPTIONAL - comma separate if multiple ids
);
NOTE: 'duration' DOES work when creating a meeting, so if you set duration
when creating a meeting, it will kick users out after the duration. Should
probably be required in user code when 'recording' is set to true.
*/
$xml = $this->_processXmlResponse($this->getRecordingsUrl($recordingParams));
if($xml) {
// If we don't get a success code or messageKey, find out why:
if (($xml->returncode != 'SUCCESS') || ($xml->messageKey == null)) {
$result = array(
'returncode' => $xml->returncode,
'messageKey' => $xml->messageKey,
'message' => $xml->message
);
return $result;
}
else {
// In this case, we have success and recording info:
$result = array(
'returncode' => $xml->returncode,
'messageKey' => $xml->messageKey,
'message' => $xml->message
);
foreach ($xml->recordings->recording as $r) {
$result[] = array(
'recordId' => $r->recordID,
'meetingId' => $r->meetingID,
'name' => $r->name,
'published' => $r->published,
'startTime' => $r->startTime,
'endTime' => $r->endTime,
'playbackFormatType' => $r->playback->format->type,
'playbackFormatUrl' => $r->playback->format->url,
'playbackFormatLength' => $r->playback->format->length,
'metadataTitle' => $r->metadata->title,
'metadataSubject' => $r->metadata->subject,
'metadataDescription' => $r->metadata->description,
'metadataCreator' => $r->metadata->creator,
'metadataContributor' => $r->metadata->contributor,
'metadataLanguage' => $r->metadata->language,
// Add more here as needed for your app depending on your
// use of metadata when creating recordings.
);
}
return $result;
}
}
else {
return null;
}
}
public function getPublishRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
'publish' => 'true', -- REQUIRED - boolean: true/false
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/publishRecordings?";
$params =
'recordID='.urlencode($recordingParams['recordId']).
'&publish='.urlencode($recordingParams['publish']);
return ($recordingsUrl.$params.'&checksum='.sha1("publishRecordings".$params.$this->_securitySalt));
}
public function publishRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
'publish' => 'true', -- REQUIRED - boolean: true/false
);
*/
$xml = $this->_processXmlResponse($this->getPublishRecordingsUrl($recordingParams));
if($xml) {
return array(
'returncode' => $xml->returncode,
'published' => $xml->published // -- Returns true/false.
);
}
else {
return null;
}
}
public function getDeleteRecordingsUrl($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
);
*/
$recordingsUrl = $this->_bbbServerBaseUrl."api/deleteRecordings?";
$params =
'recordID='.urlencode($recordingParams['recordId']);
return ($recordingsUrl.$params.'&checksum='.sha1("deleteRecordings".$params.$this->_securitySalt));
}
public function deleteRecordingsWithXmlResponseArray($recordingParams) {
/* USAGE:
$recordingParams = array(
'recordId' => '1234', -- REQUIRED - comma separate if multiple ids
);
*/
$xml = $this->_processXmlResponse($this->getDeleteRecordingsUrl($recordingParams));
if($xml) {
return array(
'returncode' => $xml->returncode,
'deleted' => $xml->deleted // -- Returns true/false.
);
}
else {
return null;
}
}
} // END OF BIGBLUEBUTTON CLASS
?>

View File

@ -1,9 +0,0 @@
<?php
// BASE CONFIGS (set these to match the values from your BBB server)
/* Public test server values from Blind Side Networks:
url: http://test-install.blindsidenetworks.com/bigbluebutton/
salt: 8cd8ef52e8e101574e400365b55e11a6
*/
define("CONFIG_SECURITY_SALT", "8cd8ef52e8e101574e400365b55e11a6");
define("CONFIG_SERVER_BASE_URL", "http://test-install.blindsidenetworks.com/bigbluebutton/");
?>

View File

@ -1,207 +0,0 @@
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
<title>bbb-api-php</title>
<link type="text/css" rel="stylesheet" href="css/main.css">
</head>
<body id="index" onload="">
<div id="main">
<h1>PHP API Examples for Big Blue Button</h1>
<p>This page includes basic documentation, and links to sample pages that use the Big Blue Button API via a PHP bridge. You can read this page and also inspect the included files to see how it works. Original documentation for the base Big Blue Button API itself can be found at the following URL:</p><p><a href="http://code.google.com/p/bigbluebutton/wiki/API">http://code.google.com/p/bigbluebutton/wiki/API</a></p>
<div id="TOC">
<div id="tocheader">Table of Contents</div>
<ul>
<li><b>Administration Methods</b></li>
<ul>
<li><a href="#createameeting">Create a Meeting</a></li>
<li><a href="#joinameeting">Join a Meeting</a></li>
<li><a href="#endameeting">End a Meeting</a></li>
</ul>
<li><b>Monitoring Methods</b></li>
<ul>
<li><a href="#ismeetingrunning">Is Meeting Running</a></li>
<li><a href="#getmeetings">Get Meetings</a></li>
<li><a href="#getmeetinginfo">Get Meeting Info</a></li>
</ul>
<li><b>Recording Methods</b></li>
<ul>
<li><a href="#getrecordings">Get Recordings</a></li>
<li><a href="#publishrecordings">Publish Recordings</a></li>
<li><a href="#deleterecordings">Delete Recordings</a></li>
</ul>
</ul>
</div>
<h2>Install and Configuration</h2>
<dl>
<dt>Install</dt><dd>Stick the entire <b>/bbb-api-php</b> directory somewhere that you can host php web files. You need to enable the 'allow_url_fopen' to 'On' in your php.ini file so these examples can work. Simply add/replace to your php.ini file: allow_url_fopen = On.</dd>
<dt>Configuration</dt><dd>Define your Big Blue Button server URL and SALT in the configuration file located at <b>/bbb-api-php/includes/config.php</b>.</dd>
<dt>Usage and Customization</dt><dd>After defining your BBB server URL and SALT in the config file, read this page and click on the links to the examples for each method below. You can either modify the included files directly until they do what you need, or you can create your own custom app using the code in these examples to get started.</dd>
<dt>Web Framework Integration</dt><dd>You can integrate this code into a web framework like Zend Framework. Just stick the /includes/bbb-api.php file in Zend's library directory so it is included in your app. You can put then move the BBB server URL and SALT values into your app's config file and call those values from the bbb-api.php file. Last, add logic like you see in the examples below into your controllers and forms.</dd>
</dl>
<h2>Big Blue Button PHP API Usage</h2>
<h3>Administration Methods</h3>
<dl>
<a name="createameeting">&nbsp;</a>
<dt>Create a Meeting</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">createMeetingWithXmlResponseArray($creationParams)</span></p></div>
<p>To create a meeting, require the bbb-api.php file, then instatiate the BigBlueButton class in your code.</p>
<div class="code"><pre>
require_once('../includes/bbb-api.php'); //Make this match your actual path the bbb-api.php file.
$bbb = new BigBlueButton();
</pre></div>
<p>Set your meeting values in the creationParams array. (You'll probably grab these from a user-submitted form in real life.) Note that meetingId and meetingName are required. The rest are optional. In the example below, we set attendeePw to be "pw" and moderatorPw to be "mp". This facilititates accessing information about this meeting later by passing matching values. In real code, you'd use PHP to generate random values for those passwords and then store them in your code to access later.</p>
<div class="code"><pre>
$creationParams = array(
'meetingId' => '1234', // REQUIRED
'meetingName' => 'Test Meeting Name', // REQUIRED
'attendeePw' => 'ap', // Match this value in getJoinMeetingURL() to join as attendee.
'moderatorPw' => 'mp', // Match this value in getJoinMeetingURL() to join as moderator.
'welcomeMsg' => '', // ''= use default. Change to customize.
'dialNumber' => '', // The main number to call into. Optional.
'voiceBridge' => '12345', // 5 digit PIN to join voice bridge. Required.
'webVoice' => '', // Alphanumeric to join voice. Optional.
'logoutUrl' => '', // Default in bigbluebutton.properties. Optional.
'maxParticipants' => '-1', // Optional. -1 = unlimitted. Not supported in BBB. [number]
'record' => 'false', // New. 'true' will tell BBB to record the meeting.
'duration' => '0', // Default = 0 which means no set duration in minutes. [number]
//'meta_category' => '', // Use to pass additional info to BBB server. See API docs.
);
</pre></div>
<p>Finally, create the meeting by calling the createMeetingWithXmlResponse method and passing it your desired parameters that you set up in the creationParams array. $result will be an array that contains the XML response from your Big Blue Button server.</p>
<div class="code"><pre>
$result = $bbb->createMeetingWithXmlResponseArray($creationParams);
</pre></div>
<p>That's it. As you can see in the examples, you can add additional code to handle errors and exceptions or do other things as needed. The rest of the methods work basically the same way. You can view the source in the examples or in the bbb-api.php file to see which parameters to pass to the methods.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/createMeeting.php">Create a meeting</a> with a meetingId of '1234' and a meetingName of 'Test Meeting Name' now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/createMeeting.php</b>.</p>
</div>
</dd>
<a name="joinameeting">&nbsp;</a>
<dt>Join a Meeting</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">getJoinMeetingURL($joinParams)</span></p></div>
<p>Note that this method does not return an XML response from the BBB server. Instead it returns the URL that people can use to join a meeting. You can send this URL to people in an email, or write your code to automatically redirect them there depending on your needs.</p>
<p>Note that when testing, you must create the '1234' meeting using the createMeetingWithXmlResponseArray method above before you can successfully join it.</p>
<div class="example">
<p><b>Try it out</b>:<br /><a href="examples/getJoinMeetingUrlModerator.php">Get the URL</a> to join a meeting with a meetingId of '1234' as a Moderator now.<br />
<a href="examples/getJoinMeetingUrlAttendee.php">Get the URL</a> to join a meeting with a meetingId of '1234' as an Attendee now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/getJoinMeetingUrlModerator.php</b> and <br /> <b>examples/getJoinMeetingUrlModerator.php</b>.</p>
<p><b>Bonus</b>: <a href="examples/joinIfRunning.php">Join only once running</a> a meeting with a meetingId of '1234' as an Attendee now. This page shows one way to keep the user on a spinner page until the meeting is started by the moderator. You can inspect the code in <b>/bbb-api-php/examples/joinIfRunning.php</b> to learn more about this.</p>
</div>
</dd>
<a name="endameeting">&nbsp;</a>
<dt>End a Meeting</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">endMeetingWithXmlResponseArray($endParams)</span></p></div>
<p>Note that when testing, you must create and start (join as moderator) the '1234' meeting using the methods listed above before you can successfully end it.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/endMeeting.php">End a meeting</a> with a meetingId of '1234' now.</p>
<p><b>Learn more</b>: Read the source code in <b>/bbb-api-php/examples/endMeeting.php</b>.</p>
</div>
</dd>
</dl>
<h3>Monitoring Methods</h3>
<dl>
<a name="ismeetingrunning">&nbsp;</a>
<dt>Is Meeting Running</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">isMeetingRunningWithXmlResponseArray($meetingId)</span></p></div>
<p>This method will return whether the meeting is currently running or not.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/isMeetingRunning.php">Determine if meeting is running</a> for a meeting with a meetingId of '1234' now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/isMeetingRunning.php</b>.</p>
</div>
</dd>
<a name="getmeetings">&nbsp;</a>
<dt>Get Meetings</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">getMeetingsWithXmlResponseArray()</span></p></div>
<p>This method will retrieve a list of current meetings on the BBB server.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/getMeetings.php">Get all meetings</a> now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/getMeetings.php</b>.</p>
</div>
</dd>
<a name="getmeetinginfo">&nbsp;</a>
<dt>Get Meeting Info</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">getMeetingInfoWithXmlResponseArray($infoParams)</span></p></div>
<p>This method will return information about a meeting.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/getMeetingInfo.php">Get meeting info</a> for a meeting with a meetingId of '1234' now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/getMeetingInfo.php</b>.</p>
</div>
</dd>
</dl>
<h3>Recording Methods</h3>
<p><b>Convenience pages for recording</b>: <a href="examples/createRecordedMeeting.php">Create a meeting</a> with 'record' set to 'true' now. This will create a 5 minute long meeting that will be recorded once you <a href="examples/getJoinMeetingUrlModeratorRecord.php">join into it as moderator and let it run</a>. (The meeting will stop after 5 minutes.). The meetingId for this recorded meeting will be <b>'12345'</b>. Once recorded, you can use the methods below to test your results. To get success with publish and delete methods, you'll need to get back a valid recordID using the getRecordingsWithXmlResponseArray() method first, and then add the recordID to the example pages for publishing and deleting recordings.
<dl>
<a name="getrecordings">&nbsp;</a>
<dt>Get Recordings</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">getRecordingsWithXmlResponseArray($recordingParams)</span></p></div>
<p>This method will return information for specific recordings if you pass it one or more comma separated meetingIds. By default, with no meetingIds, it will list all recordings.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/getRecordings.php">Get all recordings</a> now.</p>
<p><b>Learn more</b>: Read the source code in <b>examples/getRecordings.php</b>.</p>
</div>
</dd>
<a name="publishrecordings">&nbsp;</a>
<dt>Publish Recordings</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">publishRecordingsWithXmlResponseArray($recordingParams)</span></p></div>
<p>This method publishes/unpublishes recordings.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/publishRecordings.php">Publish recordings</a> now. (Note that you must get a valid recordID from getRecordingsWithXmlResponseArray() and enter it as the <b>$recordingParams['recordId']</b> value in <b>examples/publishRecordings.php</b> in order to get success here. The default recordID that ships in that page won't work on your server.)</p>
<p><b>Learn more</b>: Read the source code in <b>examples/publishRecordings.php</b>.</p>
</div>
</dd>
<a name="deleterecordings">&nbsp;</a>
<dt>Delete Recordings</dt>
<dd>
<div class="method"><p>METHOD: <span class="methodname">deleteRecordingsWithXmlResponseArray($recordingParams)</span></p></div>
<p>This method deletes recordings.</p>
<div class="example">
<p><b>Try it out</b>: <a href="examples/deleteRecordings.php">Delete recordings</a> now. (Note that you must get a valid recordID from getRecordingsWithXmlResponseArray() and enter it as the <b>$recordingParams['recordId']</b> value in <b>examples/deleteRecordings.php</b> in order to get success here. The default recordID that ships in that page won't work on your server.)</p>
<p><b>Learn more</b>: Read the source code in <b>examples/deleteRecordings.php</b>.</p>
</div>
</dd>
</dl>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>
</body>
</html>

View File

@ -60,6 +60,22 @@ if not FileTest.directory?(target_dir)
FileUtils.mkdir_p temp_dir
FileUtils.cp_r(raw_archive_dir, temp_dir)
# Create initial metadata.xml
b = Builder::XmlMarkup.new(:indent => 2)
metaxml = b.recording {
b.id(meeting_id)
b.state("processing")
b.published(false)
b.start_time
b.end_time
b.playback
b.meta
}
metadata_xml = File.new("#{target_dir}/metadata.xml","w")
metadata_xml.write(metaxml)
metadata_xml.close
BigBlueButton.logger.info("Created inital metadata.xml")
BigBlueButton::AudioProcessor.process("#{temp_dir}/#{meeting_id}", "#{target_dir}/audio")
events_xml = "#{temp_dir}/#{meeting_id}/events.xml"
FileUtils.cp(events_xml, target_dir)
@ -80,25 +96,33 @@ if not FileTest.directory?(target_dir)
real_start_time = match[1]
real_end_time = (real_start_time.to_i + (meeting_end.to_i - meeting_start.to_i)).to_s
# Create initial metadata.xml
b = Builder::XmlMarkup.new(:indent => 2)
metaxml = b.recording {
b.id(meeting_id)
b.state("processing")
b.published(false)
# Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
b.start_time(real_start_time)
b.end_time(real_end_time)
b.playback
b.meta {
BigBlueButton::Events.get_meeting_metadata("#{target_dir}/events.xml").each { |k,v| b.method_missing(k,v) }
# Add start_time, end_time and meta to metadata.xml
## Load metadata.xml
metadata = Nokogiri::XML(File.open("#{target_dir}/metadata.xml"))
## Add start_time and end_time
recording = metadata.root
### Date Format for recordings: Thu Mar 04 14:05:56 UTC 2010
start_time = recording.at_xpath("start_time")
start_time.content = real_start_time
end_time = recording.at_xpath("end_time")
end_time.content = real_end_time
## Remove empty meta
metadata.search('//recording/meta').each do |meta|
meta.remove
end
## Add the actual meta
metadata_with_playback = Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml|
xml.meta {
BigBlueButton::Events.get_meeting_metadata("#{target_dir}/events.xml").each { |k,v| xml.method_missing(k,v) }
}
}
metadata_xml = File.new("#{target_dir}/metadata.xml","w")
metadata_xml.write(metaxml)
metadata_xml.close
BigBlueButton.logger.info("Created inital metadata.xml")
end
## Write the new metadata.xml
metadata_file = File.new("#{target_dir}/metadata.xml","w")
metadata = Nokogiri::XML(metadata.to_xml) { |x| x.noblanks }
metadata_file.write(metadata.root)
metadata_file.close
BigBlueButton.logger.info("Created an updated metadata.xml with start_time and end_time")
# Start processing raw files
presentations.each do |pres|
@ -157,7 +181,7 @@ if not FileTest.directory?(target_dir)
process_done.write("Processed #{meeting_id}")
process_done.close
# Build updated metadata.xml
# Update state in metadata.xml
## Load metadata.xml
metadata = Nokogiri::XML(File.open("#{target_dir}/metadata.xml"))
## Update status
@ -165,9 +189,9 @@ if not FileTest.directory?(target_dir)
state = recording.at_xpath("state")
state.content = "processed"
## Write the new metadata.xml
metadata_xml = File.new("#{target_dir}/metadata.xml","w")
metadata_xml.write(metadata.root)
metadata_xml.close
metadata_file = File.new("#{target_dir}/metadata.xml","w")
metadata_file.write(metadata.root)
metadata_file.close
BigBlueButton.logger.info("Created an updated metadata.xml with state=processed")
rescue Exception => e
@ -178,4 +202,3 @@ if not FileTest.directory?(target_dir)
exit 1
end
end

View File

@ -467,7 +467,7 @@ def storePollResultShape(xml, shape)
end
File.open(gpl_file, 'w') do |g|
g.puts('reset')
g.puts("set term pdfcairo size #{height / 72}, #{width / 72} font \"Arial,48\"")
g.puts("set term pdfcairo size #{height / 72}, #{width / 72} font \"Arial,48\" noenhanced")
g.puts('unset key')
g.puts('set style data boxes')
g.puts('set style fill solid border -1')
@ -475,7 +475,7 @@ def storePollResultShape(xml, shape)
g.puts('set yrange [0:*]')
g.puts('unset border')
g.puts('unset ytics')
xtics = result.map{ |r| "\"#{r['key'].gsub('%', '%%')}\" #{r['id']}" }.join(', ')
xtics = result.map{ |r| "#{r['key'].gsub('%', '%%').inspect} #{r['id']}" }.join(', ')
g.puts("set xtics rotate by 90 scale 0 right (#{xtics})")
if num_responders > 0
x2tics = result.map{ |r| "\"#{(r['num_votes'].to_f / num_responders * 100).to_i}%%\" #{r['id']}" }.join(', ')
@ -972,8 +972,8 @@ begin
# presentation_url = "/slides/" + $meeting_id + "/presentation"
@doc = Nokogiri::XML(File.open("#{$process_dir}/events.xml"))
#$meeting_start = @doc.xpath("//event")[0][:timestamp]
#$meeting_end = @doc.xpath("//event").last()[:timestamp]
$meeting_start = @doc.xpath("//event")[0][:timestamp]
$meeting_end = @doc.xpath("//event").last()[:timestamp]
## These $version variables are not used anywere in this code ##
$version = BigBlueButton::Events.bbb_version("#{$process_dir}/events.xml")
@ -990,18 +990,20 @@ begin
FileUtils.cp("#{$process_dir}/metadata.xml", package_dir)
BigBlueButton.logger.info("Copied metadata.xml file")
# Add playback to metadata.xml
# Update state and add playback to metadata.xml
## Load metadata.xml
metadata = Nokogiri::XML(File.open("#{package_dir}/metadata.xml"))
## Update status
## Update state
recording = metadata.root
state = recording.at_xpath("state")
state.content = "published"
published = recording.at_xpath("published")
published.content = "true"
## Remove empty playback
metadata.search('//recording/playback').each do |playback|
playback.remove
end
## Update status and add the actual playback
## Add the actual playback
metadata_with_playback = Nokogiri::XML::Builder.with(metadata.at('recording')) do |xml|
xml.playback {
xml.format("presentation")
@ -1010,18 +1012,15 @@ begin
xml.duration("#{recording_time}")
}
end
BigBlueButton.logger.info(metadata.to_xml)
## Write the new metadata.xml
metadata_xml = File.new("#{package_dir}/metadata.xml","w")
metadata_file = File.new("#{package_dir}/metadata.xml","w")
metadata = Nokogiri::XML(metadata.to_xml) { |x| x.noblanks }
metadata_xml.write(metadata.root)
metadata_xml.close
metadata_file.write(metadata.root)
metadata_file.close
BigBlueButton.logger.info("Added playback to metadata.xml")
BigBlueButton.logger.info("Generating xml for slides and chat")
#Create slides.xml
BigBlueButton.logger.info("Generating xml for slides and chat")
# Gathering all the events from the events.xml
$slides_events = @doc.xpath("//event[@eventname='GotoSlideEvent' or @eventname='SharePresentationEvent']")