Merge remote-tracking branch 'upstream/master' into settings-accessibility
@ -28,18 +28,18 @@ object JsonTest {
|
||||
//> xroom3 : org.bigbluebutton.core.api.BreakoutRoomInPayload = BreakoutRoomInP
|
||||
//| ayload(baz,Vector(q, r, s))
|
||||
|
||||
val xmsg = new CreateBreakoutRooms("test-meeting", 10, Vector(xroom1, xroom2, xroom3))
|
||||
val xmsg = new CreateBreakoutRooms("test-meeting", 10, false, Vector(xroom1, xroom2, xroom3))
|
||||
//> xmsg : org.bigbluebutton.core.api.CreateBreakoutRooms = CreateBreakoutRoom
|
||||
//| s(test-meeting,10,Vector(BreakoutRoomInPayload(foo,Vector(a, b, c)), Breako
|
||||
//| utRoomInPayload(bar,Vector(x, y, z)), BreakoutRoomInPayload(baz,Vector(q, r
|
||||
//| , s))))
|
||||
//| s(test-meeting,10,false,Vector(BreakoutRoomInPayload(foo,Vector(a, b, c)),
|
||||
//| BreakoutRoomInPayload(bar,Vector(x, y, z)), BreakoutRoomInPayload(baz,Vecto
|
||||
//| r(q, r, s))))
|
||||
|
||||
val xjsonAst = xmsg.toJson //> xjsonAst : spray.json.JsValue = {"meetingId":"test-meeting","durationInMin
|
||||
//| utes":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users
|
||||
//| ":["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
//| utes":10,"record":false,"rooms":[{"name":"foo","users":["a","b","c"]},{"nam
|
||||
//| e":"bar","users":["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
val xjson = xjsonAst.asJsObject //> xjson : spray.json.JsObject = {"meetingId":"test-meeting","durationInMinut
|
||||
//| es":10,"rooms":[{"name":"foo","users":["a","b","c"]},{"name":"bar","users":
|
||||
//| ["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
//| es":10,"record":false,"rooms":[{"name":"foo","users":["a","b","c"]},{"name"
|
||||
//| :"bar","users":["x","y","z"]},{"name":"baz","users":["q","r","s"]}]}
|
||||
val meetingId = for {
|
||||
meetingId <- xjson.fields.get("meetingId")
|
||||
} yield meetingId //> meetingId : Option[spray.json.JsValue] = Some("test-meeting")
|
||||
@ -53,10 +53,7 @@ object JsonTest {
|
||||
//| den","Yaya Dub"]}],"durationInMinutes":20}}
|
||||
//| "
|
||||
|
||||
JsonMessageDecoder.decode(cbrm) //> res0: Option[org.bigbluebutton.core.api.InMessage] = Some(CreateBreakoutRoo
|
||||
//| ms(abc123,20,Vector(BreakoutRoomInPayload(room1,Vector(Tidora, Nidora, Tini
|
||||
//| dora)), BreakoutRoomInPayload(room2,Vector(Jose, Wally, Paolo)), BreakoutRo
|
||||
//| omInPayload(room3,Vector(Alden, Yaya Dub)))))
|
||||
JsonMessageDecoder.decode(cbrm) //> res0: Option[org.bigbluebutton.core.api.InMessage] = None
|
||||
val rbju = """
|
||||
{"header":{"name":"RequestBreakoutJoinURL"},"payload":{"userId":"id6pa5t8m1c9_1","meetingId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357","breakoutId":"183f0bf3a0982a127bdb8161e0c44eb696b3e75c-1452692983357-2"}}
|
||||
""" //> rbju : String = "
|
||||
@ -91,10 +88,10 @@ object JsonTest {
|
||||
val vector = Vector(jsObj) //> vector : scala.collection.immutable.Vector[spray.json.JsObject] = Vector({
|
||||
//| "name":"Breakout Room","breakoutId":"br-id-1"})
|
||||
|
||||
val brlum = new BreakoutRoomsListOutMessage("main-meeting-1", Vector(brb))
|
||||
val brlum = new BreakoutRoomsListOutMessage("main-meeting-1", Vector(brb), false)
|
||||
//> brlum : org.bigbluebutton.core.api.BreakoutRoomsListOutMessage = BreakoutR
|
||||
//| oomsListOutMessage(main-meeting-1,Vector(BreakoutRoomBody(Breakout Room,br-
|
||||
//| id-1)))
|
||||
//| id-1)),false)
|
||||
var roomsJsVector: ListBuffer[JsObject] = new ListBuffer[JsObject]()
|
||||
//> roomsJsVector : scala.collection.mutable.ListBuffer[spray.json.JsObject] =
|
||||
//| ListBuffer()
|
||||
@ -112,9 +109,10 @@ object JsonTest {
|
||||
//| ]
|
||||
|
||||
MeetingMessageToJsonConverter.breakoutRoomsListOutMessageToJson(brlum);
|
||||
//> res5: String = {"payload":{"meetingId":"main-meeting-1","rooms":[{"name":"B
|
||||
//| reakout Room","breakoutId":"br-id-1"}]},"header":{"timestamp":47333494,"nam
|
||||
//| e":"BreakoutRoomsList","current_time":1453399680291,"version":"0.0.1"}}
|
||||
//> res5: String = {"payload":{"meetingId":"main-meeting-1","roomsReady":false,
|
||||
//| "rooms":[{"name":"Breakout Room","breakoutId":"br-id-1"}]},"header":{"times
|
||||
//| tamp":9652644,"name":"BreakoutRoomsList","current_time":1471504366540,"vers
|
||||
//| ion":"0.0.1"}}
|
||||
|
||||
// JsonMessageDecoder.unmarshall(cbrm) match {
|
||||
// case Success(validMsg) => println(validMsg)
|
||||
|
@ -40,6 +40,7 @@ public interface IBigBlueButtonInGW {
|
||||
void userLeft(String meetingID, String userID, String sessionId);
|
||||
void userJoin(String meetingID, String userID, String authToken);
|
||||
void getCurrentPresenter(String meetingID, String requesterID);
|
||||
void checkIfAllowedToShareDesktop(String meetingID, String userID);
|
||||
void assignPresenter(String meetingID, String newPresenterID, String newPresenterName, String assignedBy);
|
||||
void setRecordingStatus(String meetingId, String userId, Boolean recording);
|
||||
void getRecordingStatus(String meetingId, String userId);
|
||||
@ -114,7 +115,7 @@ public interface IBigBlueButtonInGW {
|
||||
void editCaptionHistory(String meetingID, String userID, Integer startIndex, Integer endIndex, String locale, String text);
|
||||
|
||||
// DeskShare
|
||||
void deskShareStarted(String conferenceName, String callerId, String callerIdName);
|
||||
void deskShareStarted(String confId, String callerId, String callerIdName);
|
||||
void deskShareStopped(String conferenceName, String callerId, String callerIdName);
|
||||
void deskShareRTMPBroadcastStarted(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
void deskShareRTMPBroadcastStopped(String conferenceName, String streamname, int videoWidth, int videoHeight, String timestamp);
|
||||
|
@ -33,6 +33,9 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
case UserLeavingMessage.USER_LEAVING:
|
||||
processUserLeavingMessage(message);
|
||||
break;
|
||||
case AllowUserToShareDesktopRequest.NAME:
|
||||
processAllowUserToShareDesktopRequest(message);
|
||||
break;
|
||||
case AssignPresenterRequestMessage.ASSIGN_PRESENTER_REQUEST:
|
||||
processAssignPresenterRequestMessage(message);
|
||||
break;
|
||||
@ -194,6 +197,13 @@ public class UsersMessageReceiver implements MessageHandler{
|
||||
}
|
||||
}
|
||||
|
||||
private void processAllowUserToShareDesktopRequest(String message) {
|
||||
AllowUserToShareDesktopRequest msg = AllowUserToShareDesktopRequest.fromJson(message);
|
||||
if (msg != null) {
|
||||
bbbInGW.checkIfAllowedToShareDesktop(msg.meetingId, msg.userId);
|
||||
}
|
||||
}
|
||||
|
||||
private void processAssignPresenterRequestMessage(String message) {
|
||||
AssignPresenterRequestMessage apm = AssignPresenterRequestMessage.fromJson(message);
|
||||
if (apm != null) {
|
||||
|
@ -37,11 +37,6 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
case msg: UserMutedInVoiceConfMessage => handleUserMutedInVoiceConfMessage(msg)
|
||||
case msg: UserTalkingInVoiceConfMessage => handleUserTalkingInVoiceConfMessage(msg)
|
||||
case msg: VoiceConfRecordingStartedMessage => handleVoiceConfRecordingStartedMessage(msg)
|
||||
case msg: DeskShareStartedRequest => handleDeskShareStartedRequest(msg)
|
||||
case msg: DeskShareStoppedRequest => handleDeskShareStoppedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest => handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest => handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest => handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
@ -151,6 +146,7 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
/** Subscribe to meeting and voice events. **/
|
||||
eventBus.subscribe(m.actorRef, m.mProps.meetingID)
|
||||
eventBus.subscribe(m.actorRef, m.mProps.voiceBridge)
|
||||
eventBus.subscribe(m.actorRef, m.mProps.deskshareBridge)
|
||||
|
||||
meetings += m.mProps.meetingID -> m
|
||||
outGW.send(new MeetingCreated(m.mProps.meetingID, m.mProps.externalMeetingID, m.mProps.recorded, m.mProps.meetingName,
|
||||
@ -206,52 +202,5 @@ class BigBlueButtonActor(val system: ActorSystem,
|
||||
outGW.send(new GetAllMeetingsReply(resultArray))
|
||||
}
|
||||
|
||||
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
|
||||
log.info("handleDeskShareStartedRequest: msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareStartedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
|
||||
log.info("handleDeskShareStoppedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareStoppedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStartedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest msg.conferenceName=" + msg.conferenceName)
|
||||
findMeetingWithVoiceConfId(msg.conferenceName) foreach { m =>
|
||||
{
|
||||
// println(msg.conferenceName + " (in for each) handleDeskShareRTMPBroadcastStoppedRequest BBBActor ")
|
||||
m.actorRef ! msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
val m = meetings.values.find(m => {
|
||||
m.mProps.meetingID == msg.conferenceName
|
||||
})
|
||||
m foreach { mActor => mActor.actorRef ! msg }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -60,6 +60,7 @@ class BigBlueButtonInGW(
|
||||
msg.payload.name,
|
||||
msg.payload.record,
|
||||
msg.payload.voiceConfId,
|
||||
msg.payload.voiceConfId + "-DESKSHARE", // WebRTC Desktop conference id
|
||||
msg.payload.durationInMinutes,
|
||||
msg.payload.autoStartRecording,
|
||||
msg.payload.allowStartStopRecording,
|
||||
@ -242,6 +243,11 @@ class BigBlueButtonInGW(
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new UserJoining(meetingID, userID, authToken)))
|
||||
}
|
||||
|
||||
def checkIfAllowedToShareDesktop(meetingID: String, userID: String): Unit = {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, AllowUserToShareDesktop(meetingID: String,
|
||||
userID: String)))
|
||||
}
|
||||
|
||||
def assignPresenter(meetingID: String, newPresenterID: String, newPresenterName: String, assignedBy: String): Unit = {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingID, new AssignPresenter(meetingID, newPresenterID, newPresenterName, assignedBy)))
|
||||
}
|
||||
@ -498,8 +504,11 @@ class BigBlueButtonInGW(
|
||||
* Message Interface for DeskShare
|
||||
* *****************************************************************
|
||||
*/
|
||||
def deskShareStarted(meetingId: String, callerId: String, callerIdName: String) {
|
||||
eventBus.publish(BigBlueButtonEvent(meetingId, new DeskShareStartedRequest(meetingId, callerId, callerIdName)))
|
||||
def deskShareStarted(confId: String, callerId: String, callerIdName: String) {
|
||||
println("____BigBlueButtonInGW::deskShareStarted " + confId + callerId + " " +
|
||||
callerIdName)
|
||||
eventBus.publish(BigBlueButtonEvent(confId, new DeskShareStartedRequest(confId, callerId,
|
||||
callerIdName)))
|
||||
}
|
||||
|
||||
def deskShareStopped(meetingId: String, callerId: String, callerIdName: String) {
|
||||
|
@ -86,7 +86,7 @@ class JsonMessageSenderActor(val service: MessageSender)
|
||||
private def handleBreakoutRoomsList(msg: BreakoutRoomsListOutMessage) {
|
||||
val rooms = new java.util.ArrayList[BreakoutRoomPayload]()
|
||||
msg.rooms.foreach(r => rooms.add(new BreakoutRoomPayload(msg.meetingId, r.breakoutId, r.name)))
|
||||
val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms)
|
||||
val payload = new BreakoutRoomsListPayload(msg.meetingId, rooms, msg.roomsReady)
|
||||
val request = new BreakoutRoomsList(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
@ -94,7 +94,7 @@ class JsonMessageSenderActor(val service: MessageSender)
|
||||
private def handleCreateBreakoutRoom(msg: CreateBreakoutRoom) {
|
||||
val payload = new CreateBreakoutRoomRequestPayload(msg.room.breakoutId, msg.room.parentId, msg.room.name,
|
||||
msg.room.voiceConfId, msg.room.viewerPassword, msg.room.moderatorPassword,
|
||||
msg.room.durationInMinutes, msg.room.defaultPresentationURL, msg.room.recordType)
|
||||
msg.room.durationInMinutes, msg.room.defaultPresentationURL, msg.room.record)
|
||||
val request = new CreateBreakoutRoomRequest(payload)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, request.toJson())
|
||||
}
|
||||
|
@ -125,6 +125,16 @@ class LiveMeeting(val mProps: MeetingProperties,
|
||||
outGW.send(new DisconnectAllUsers(msg.meetingId))
|
||||
}
|
||||
|
||||
def handleAllowUserToShareDesktop(msg: AllowUserToShareDesktop): Unit = {
|
||||
usersModel.getCurrentPresenter() match {
|
||||
case Some(curPres) => {
|
||||
val allowed = msg.userID equals (curPres.userID)
|
||||
outGW.send(AllowUserToShareDesktopOut(msg.meetingID, msg.userID, allowed))
|
||||
}
|
||||
case None => // do nothing
|
||||
}
|
||||
}
|
||||
|
||||
def handleVoiceConfRecordingStartedMessage(msg: VoiceConfRecordingStartedMessage) {
|
||||
if (msg.recording) {
|
||||
meetingModel.setVoiceRecordingFilename(msg.recordStream)
|
||||
@ -163,4 +173,80 @@ class LiveMeeting(val mProps: MeetingProperties,
|
||||
def permissionsEqual(other: Permissions): Boolean = {
|
||||
meetingModel.permissionsEqual(other)
|
||||
}
|
||||
|
||||
// WebRTC Desktop Sharing
|
||||
|
||||
def handleDeskShareStartedRequest(msg: DeskShareStartedRequest): Unit = {
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
||||
if (!meetingModel.getDeskShareStarted()) {
|
||||
val timestamp = System.currentTimeMillis().toString
|
||||
val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
|
||||
"/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
|
||||
log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
|
||||
|
||||
// Tell FreeSwitch to broadcast to RTMP
|
||||
outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
|
||||
|
||||
meetingModel.setDeskShareStarted(true)
|
||||
}
|
||||
}
|
||||
|
||||
def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest): Unit = {
|
||||
log.info("handleDeskShareStoppedRequest: dsStarted=" + meetingModel.getDeskShareStarted() +
|
||||
" URL:" + meetingModel.getRTMPBroadcastingUrl())
|
||||
|
||||
// Tell FreeSwitch to stop broadcasting to RTMP
|
||||
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, meetingModel.getRTMPBroadcastingUrl()))
|
||||
|
||||
meetingModel.setDeskShareStarted(false)
|
||||
}
|
||||
|
||||
def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest): Unit = {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP() +
|
||||
" URL:" + meetingModel.getRTMPBroadcastingUrl())
|
||||
|
||||
// only valid if not broadcasting yet
|
||||
if (!meetingModel.isBroadcastingRTMP()) {
|
||||
meetingModel.setRTMPBroadcastingUrl(msg.streamname)
|
||||
meetingModel.broadcastingRTMPStarted()
|
||||
meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
|
||||
meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
|
||||
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
|
||||
|
||||
// Notify viewers in the meeting that there's an rtmp stream to view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
|
||||
} else {
|
||||
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
|
||||
}
|
||||
}
|
||||
|
||||
def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest): Unit = {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + meetingModel
|
||||
.isBroadcastingRTMP() + " URL:" + meetingModel.getRTMPBroadcastingUrl())
|
||||
|
||||
// only valid if currently broadcasting
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
|
||||
meetingModel.broadcastingRTMPStopped()
|
||||
|
||||
// notify viewers that RTMP broadcast stopped
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
msg.videoWidth, msg.videoHeight, false))
|
||||
} else {
|
||||
log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
|
||||
}
|
||||
}
|
||||
|
||||
def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting="
|
||||
+ meetingModel.isBroadcastingRTMP() + " URL:" + meetingModel.getRTMPBroadcastingUrl())
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
// if the meeting has an ongoing WebRTC Deskshare session, send a notification
|
||||
outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), true))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -105,6 +105,7 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
case msg: UserJoining => liveMeeting.handleUserJoin(msg)
|
||||
case msg: UserLeaving => liveMeeting.handleUserLeft(msg)
|
||||
case msg: AssignPresenter => liveMeeting.handleAssignPresenter(msg)
|
||||
case msg: AllowUserToShareDesktop => liveMeeting.handleAllowUserToShareDesktop(msg)
|
||||
case msg: GetUsers => liveMeeting.handleGetUsers(msg)
|
||||
case msg: ChangeUserStatus => liveMeeting.handleChangeUserStatus(msg)
|
||||
case msg: EjectUserFromMeeting => liveMeeting.handleEjectUserFromMeeting(msg)
|
||||
@ -178,77 +179,13 @@ class MeetingActor(val mProps: MeetingProperties,
|
||||
case msg: UpdateCaptionOwnerRequest => liveMeeting.handleUpdateCaptionOwnerRequest(msg)
|
||||
case msg: EditCaptionHistoryRequest => liveMeeting.handleEditCaptionHistoryRequest(msg)
|
||||
|
||||
case msg: DeskShareStartedRequest => liveMeeting.handleDeskShareStartedRequest(msg)
|
||||
case msg: DeskShareStoppedRequest => liveMeeting.handleDeskShareStoppedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStartedRequest => liveMeeting.handleDeskShareRTMPBroadcastStartedRequest(msg)
|
||||
case msg: DeskShareRTMPBroadcastStoppedRequest => liveMeeting.handleDeskShareRTMPBroadcastStoppedRequest(msg)
|
||||
case msg: DeskShareGetDeskShareInfoRequest => liveMeeting.handleDeskShareGetDeskShareInfoRequest(msg)
|
||||
|
||||
case _ => // do nothing
|
||||
}
|
||||
|
||||
// Broadcast video stream,
|
||||
private def handleDeskShareStartedRequest(msg: DeskShareStartedRequest) {
|
||||
log.info("handleDeskShareStartedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
||||
if (!meetingModel.getDeskShareStarted()) {
|
||||
val timestamp = System.currentTimeMillis().toString()
|
||||
val streamPath = "rtmp://" + mProps.red5DeskShareIP + "/" + mProps.red5DeskShareApp +
|
||||
"/" + mProps.meetingID + "/" + mProps.meetingID + "-" + timestamp
|
||||
log.info("handleDeskShareStartedRequest: streamPath=" + streamPath)
|
||||
|
||||
// Tell FreeSwitch to broadcast to RTMP
|
||||
outGW.send(new DeskShareStartRTMPBroadcast(msg.conferenceName, streamPath))
|
||||
|
||||
meetingModel.setDeskShareStarted(true)
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareStoppedRequest(msg: DeskShareStoppedRequest) {
|
||||
log.info("handleDeskShareStoppedRequest: dsStarted=" + meetingModel.getDeskShareStarted())
|
||||
|
||||
// Tell FreeSwitch to stop broadcasting to RTMP
|
||||
outGW.send(new DeskShareStopRTMPBroadcast(msg.conferenceName, meetingModel.getRTMPBroadcastingUrl()))
|
||||
|
||||
meetingModel.setDeskShareStarted(false)
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStartedRequest(msg: DeskShareRTMPBroadcastStartedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStartedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
|
||||
|
||||
// only valid if not broadcasting yet
|
||||
if (!meetingModel.isBroadcastingRTMP()) {
|
||||
meetingModel.setRTMPBroadcastingUrl(msg.streamname)
|
||||
meetingModel.broadcastingRTMPStarted()
|
||||
meetingModel.setDesktopShareVideoWidth(msg.videoWidth)
|
||||
meetingModel.setDesktopShareVideoHeight(msg.videoHeight)
|
||||
log.info("START broadcast ALLOWED when isBroadcastingRTMP=false")
|
||||
|
||||
// Notify viewers in the meeting that there's an rtmp stream to view
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, msg.streamname, msg.videoWidth, msg.videoHeight, true))
|
||||
} else {
|
||||
log.info("START broadcast NOT ALLOWED when isBroadcastingRTMP=true")
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareRTMPBroadcastStoppedRequest(msg: DeskShareRTMPBroadcastStoppedRequest) {
|
||||
log.info("handleDeskShareRTMPBroadcastStoppedRequest: isBroadcastingRTMP=" + meetingModel.isBroadcastingRTMP())
|
||||
|
||||
// only valid if currently broadcasting
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
log.info("STOP broadcast ALLOWED when isBroadcastingRTMP=true")
|
||||
meetingModel.broadcastingRTMPStopped()
|
||||
|
||||
// notify viewers that RTMP broadcast stopped
|
||||
outGW.send(new DeskShareNotifyViewersRTMP(mProps.meetingID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
msg.videoWidth, msg.videoHeight, false))
|
||||
} else {
|
||||
log.info("STOP broadcast NOT ALLOWED when isBroadcastingRTMP=false")
|
||||
}
|
||||
}
|
||||
|
||||
private def handleDeskShareGetDeskShareInfoRequest(msg: DeskShareGetDeskShareInfoRequest): Unit = {
|
||||
|
||||
log.info("handleDeskShareGetDeskShareInfoRequest: " + msg.conferenceName + "isBroadcasting=" + meetingModel.isBroadcastingRTMP())
|
||||
if (meetingModel.isBroadcastingRTMP()) {
|
||||
// if the meeting has an ongoing WebRTC Deskshare session, send a notification
|
||||
outGW.send(new DeskShareNotifyASingleViewer(mProps.meetingID, msg.requesterID, meetingModel.getRTMPBroadcastingUrl(),
|
||||
meetingModel.getDesktopShareVideoWidth(), meetingModel.getDesktopShareVideoHeight(), true))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ import java.util.concurrent.TimeUnit
|
||||
|
||||
case object StopMeetingActor
|
||||
case class MeetingProperties(meetingID: String, externalMeetingID: String, meetingName: String, recorded: Boolean,
|
||||
voiceBridge: String, duration: Int, autoStartRecording: Boolean, allowStartStopRecording: Boolean,
|
||||
moderatorPass: String, viewerPass: String, createTime: Long, createDate: String,
|
||||
red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
|
||||
voiceBridge: String, deskshareBridge: String, duration: Int, autoStartRecording: Boolean,
|
||||
allowStartStopRecording: Boolean, moderatorPass: String, viewerPass: String, createTime: Long,
|
||||
createDate: String, red5DeskShareIP: String, red5DeskShareApp: String, isBreakout: Boolean)
|
||||
|
||||
case class MeetingExtensionProp(maxExtensions: Int = 2, numExtensions: Int = 0, extendByMinutes: Int = 20,
|
||||
sendNotice: Boolean = true, sent15MinNotice: Boolean = false,
|
||||
|
@ -15,7 +15,9 @@ import org.bigbluebutton.core.pubsub.senders.CaptionMessageToJsonConverter
|
||||
import org.bigbluebutton.core.pubsub.senders.DeskShareMessageToJsonConverter
|
||||
import org.bigbluebutton.common.messages.GetPresentationInfoReplyMessage
|
||||
import org.bigbluebutton.common.messages.PresentationRemovedMessage
|
||||
import org.bigbluebutton.common.messages.AllowUserToShareDesktopReply
|
||||
import org.bigbluebutton.core.apps.Page
|
||||
|
||||
import collection.JavaConverters._
|
||||
import scala.collection.JavaConversions._
|
||||
import org.bigbluebutton.core.apps.SimplePollResultOutVO
|
||||
@ -31,6 +33,7 @@ import org.bigbluebutton.common.messages.LockLayoutMessage
|
||||
import org.bigbluebutton.core.pubsub.senders.WhiteboardMessageToJsonConverter
|
||||
import org.bigbluebutton.common.converters.ToJsonEncoder
|
||||
import org.bigbluebutton.common.messages.TransferUserToVoiceConfRequestMessage
|
||||
import org.bigbluebutton.core
|
||||
|
||||
object MessageSenderActor {
|
||||
def props(msgSender: MessageSender): Props =
|
||||
@ -82,6 +85,7 @@ class MessageSenderActor(val service: MessageSender)
|
||||
case msg: MeetingMuted => handleMeetingMuted(msg)
|
||||
case msg: MeetingState => handleMeetingState(msg)
|
||||
case msg: DisconnectAllUsers => handleDisconnectAllUsers(msg)
|
||||
case msg: AllowUserToShareDesktopOut => handleAllowUserToShareDesktopOut(msg)
|
||||
case msg: DisconnectUser => handleDisconnectUser(msg)
|
||||
case msg: PermissionsSettingInitialized => handlePermissionsSettingInitialized(msg)
|
||||
case msg: NewPermissionsSetting => handleNewPermissionsSetting(msg)
|
||||
@ -535,6 +539,13 @@ class MessageSenderActor(val service: MessageSender)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handleAllowUserToShareDesktopOut(msg: AllowUserToShareDesktopOut): Unit = {
|
||||
val obj = new AllowUserToShareDesktopReply(msg.meetingID, msg.userID, msg.allowed,
|
||||
TimestampGenerator.generateTimestamp)
|
||||
val json = obj.toJson()
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, json)
|
||||
}
|
||||
|
||||
private def handlePermissionsSettingInitialized(msg: PermissionsSettingInitialized) {
|
||||
val json = UsersMessageToJsonConverter.permissionsSettingInitializedToJson(msg)
|
||||
service.send(MessagingConstants.FROM_MEETING_CHANNEL, json)
|
||||
|
@ -42,7 +42,7 @@ case class LockSetting(meetingID: String, locked: Boolean, settings: Map[String,
|
||||
// Sent by user to request the breakout rooms list of a room
|
||||
case class BreakoutRoomsListMessage(meetingId: String) extends InMessage
|
||||
// Sent by user to request creation of breakout rooms
|
||||
case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, recordType: String,
|
||||
case class CreateBreakoutRooms(meetingId: String, durationInMinutes: Int, record: Boolean,
|
||||
rooms: Vector[BreakoutRoomInPayload]) extends InMessage
|
||||
case class BreakoutRoomInPayload(name: String, users: Vector[String])
|
||||
// Sent by user to request for a join URL in order to be able to join a breakout room
|
||||
@ -90,6 +90,7 @@ case class ChangeUserStatus(meetingID: String, userID: String, status: String, v
|
||||
case class AssignPresenter(meetingID: String, newPresenterID: String, newPresenterName: String, assignedBy: String) extends InMessage
|
||||
case class SetRecordingStatus(meetingID: String, userId: String, recording: Boolean) extends InMessage
|
||||
case class GetRecordingStatus(meetingID: String, userId: String) extends InMessage
|
||||
case class AllowUserToShareDesktop(meetingID: String, userID: String) extends InMessage
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// Chat
|
||||
|
@ -28,12 +28,12 @@ case class PubSubPong(system: String, timestamp: Long) extends IOutMessage
|
||||
case object IsAliveMessage extends IOutMessage
|
||||
|
||||
// Breakout Rooms
|
||||
case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody]) extends IOutMessage
|
||||
case class BreakoutRoomsListOutMessage(meetingId: String, rooms: Vector[BreakoutRoomBody], roomsReady: Boolean) extends IOutMessage
|
||||
case class CreateBreakoutRoom(meetingId: String, room: BreakoutRoomOutPayload) extends IOutMessage
|
||||
case class EndBreakoutRoom(breakoutId: String) extends IOutMessage
|
||||
case class BreakoutRoomOutPayload(breakoutId: String, name: String, parentId: String,
|
||||
voiceConfId: String, durationInMinutes: Int, moderatorPassword: String, viewerPassword: String,
|
||||
defaultPresentationURL: String, recordType: String)
|
||||
defaultPresentationURL: String, record: Boolean)
|
||||
case class BreakoutRoomJoinURLOutMessage(meetingId: String, recorded: Boolean, breakoutId: String, userId: String, joinURL: String) extends IOutMessage
|
||||
case class BreakoutRoomStartedOutMessage(meetingId: String, recorded: Boolean, breakout: BreakoutRoomBody) extends IOutMessage
|
||||
case class BreakoutRoomBody(name: String, breakoutId: String)
|
||||
@ -73,6 +73,7 @@ case class EjectVoiceUser(meetingID: String, recorded: Boolean, requesterID: Str
|
||||
case class TransferUserToMeeting(voiceConfId: String, targetVoiceConfId: String, userId: String) extends IOutMessage
|
||||
case class UserJoinedVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
case class UserLeftVoice(meetingID: String, recorded: Boolean, confNum: String, user: UserVO) extends IOutMessage
|
||||
case class AllowUserToShareDesktopOut(meetingID: String, userID: String, allowed: Boolean) extends IOutMessage
|
||||
|
||||
// Voice
|
||||
case class IsMeetingMutedReply(meetingID: String, recorded: Boolean, requesterID: String, meetingMuted: Boolean) extends IOutMessage
|
||||
|
@ -31,11 +31,17 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
|
||||
def handleBreakoutRoomsList(msg: BreakoutRoomsListMessage) {
|
||||
val breakoutRooms = breakoutModel.getRooms().toVector map { r => new BreakoutRoomBody(r.name, r.id) }
|
||||
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms));
|
||||
outGW.send(new BreakoutRoomsListOutMessage(mProps.meetingID, breakoutRooms, breakoutModel.pendingRoomsNumber == 0 && breakoutRooms.length > 0));
|
||||
}
|
||||
|
||||
def handleCreateBreakoutRooms(msg: CreateBreakoutRooms) {
|
||||
// If breakout rooms are being created we ignore the coming message
|
||||
if (breakoutModel.pendingRoomsNumber > 0) {
|
||||
log.warning("CreateBreakoutRooms event received while {} are pending to be created for meeting {}", breakoutModel.pendingRoomsNumber, mProps.meetingID)
|
||||
return
|
||||
}
|
||||
var i = 0
|
||||
breakoutModel.pendingRoomsNumber = msg.rooms.length;
|
||||
for (room <- msg.rooms) {
|
||||
i += 1
|
||||
val presURL = bbbWebDefaultPresentationURL
|
||||
@ -44,7 +50,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
val r = breakoutModel.createBreakoutRoom(breakoutMeetingId, room.name, voiceConfId, room.users, presURL)
|
||||
val p = new BreakoutRoomOutPayload(r.id, r.name, mProps.meetingID,
|
||||
r.voiceConfId, msg.durationInMinutes, mProps.moderatorPass, mProps.viewerPass,
|
||||
r.defaultPresentationURL, msg.recordType)
|
||||
r.defaultPresentationURL, msg.record)
|
||||
outGW.send(new CreateBreakoutRoom(mProps.meetingID, p))
|
||||
}
|
||||
meetingModel.breakoutRoomsdurationInMinutes = msg.durationInMinutes;
|
||||
@ -67,16 +73,24 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
}
|
||||
|
||||
def handleBreakoutRoomCreated(msg: BreakoutRoomCreated) {
|
||||
breakoutModel.pendingRoomsNumber -= 1
|
||||
val room = breakoutModel.getBreakoutRoom(msg.breakoutRoomId)
|
||||
room foreach { room =>
|
||||
sendBreakoutRoomStarted(mProps.meetingID, room.name, room.id, room.voiceConfId)
|
||||
}
|
||||
|
||||
breakoutModel.getAssignedUsers(msg.breakoutRoomId) foreach { users =>
|
||||
users.foreach { u =>
|
||||
log.debug("## Sending Join URL for users: {}", u);
|
||||
sendJoinURL(u, msg.breakoutRoomId)
|
||||
// We avoid sending invitation
|
||||
if (breakoutModel.pendingRoomsNumber == 0) {
|
||||
log.info("All breakout rooms created for meetingId={}", mProps.meetingID)
|
||||
breakoutModel.getRooms().foreach { room =>
|
||||
breakoutModel.getAssignedUsers(room.id) foreach { users =>
|
||||
users.foreach { u =>
|
||||
log.debug("## Sending Join URL for users: {}", u);
|
||||
sendJoinURL(u, room.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
handleBreakoutRoomsList(new BreakoutRoomsListMessage(mProps.meetingID))
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,7 +130,7 @@ trait BreakoutRoomApp extends SystemConfiguration {
|
||||
else {
|
||||
targetVoiceBridge = mProps.voiceBridge.dropRight(1)
|
||||
}
|
||||
// We check the iser from the mode
|
||||
// We check the user from the mode
|
||||
usersModel.getUser(msg.userId) match {
|
||||
case Some(u) => {
|
||||
if (u.voiceUser.joined) {
|
||||
|
@ -10,6 +10,8 @@ case class BreakoutRoom(id: String, name: String, voiceConfId: String,
|
||||
class BreakoutRoomModel {
|
||||
private var rooms = new collection.immutable.HashMap[String, BreakoutRoom]
|
||||
|
||||
var pendingRoomsNumber: Integer = 0
|
||||
|
||||
def add(room: BreakoutRoom): BreakoutRoom = {
|
||||
rooms += room.id -> room
|
||||
room
|
||||
|
@ -137,6 +137,7 @@ object MeetingMessageToJsonConverter {
|
||||
val payload = new java.util.HashMap[String, Any]()
|
||||
payload.put("meetingId", msg.meetingId)
|
||||
payload.put("rooms", msg.rooms.toArray)
|
||||
payload.put("roomsReady", msg.roomsReady)
|
||||
|
||||
val header = Util.buildHeader(BreakoutRoomsList.NAME, None)
|
||||
Util.buildJson(header, payload)
|
||||
|
@ -7,6 +7,7 @@ trait AppsTestFixtures {
|
||||
val meetingName = "test meeting"
|
||||
val record = false
|
||||
val voiceConfId = "85115"
|
||||
val deskshareConfId = "85115-DESKSHARE"
|
||||
val durationInMinutes = 10
|
||||
val autoStartRecording = false
|
||||
val allowStartStopRecording = false
|
||||
@ -18,7 +19,7 @@ trait AppsTestFixtures {
|
||||
|
||||
val mProps = new MeetingProperties(meetingId, externalMeetingId,
|
||||
meetingName, record,
|
||||
voiceConfId,
|
||||
voiceConfId, deskshareConfId,
|
||||
durationInMinutes,
|
||||
autoStartRecording, allowStartStopRecording,
|
||||
moderatorPassword, viewerPassword,
|
||||
|
@ -13,20 +13,12 @@
|
||||
<include>
|
||||
<context name="default">
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}$">
|
||||
<condition field="destination_number" expression="^\d{5}-DESKSHARE$">
|
||||
<action application="log" data="INFO AAAAAA transferring to $1 XML public!!"/>
|
||||
<action application="transfer" data="${destination_number} XML public"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^(\d{5})(-screen)$">
|
||||
<action application="log" data="INFO BB $1 $2 BBBB transferring to $1 XML public!!"/>
|
||||
<action application="transfer" data="$1 XML public"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
<!-- other extensions -->
|
||||
|
||||
</context>
|
||||
|
@ -16,10 +16,10 @@
|
||||
<!-- other extensions -->
|
||||
|
||||
<extension name="public_extensions">
|
||||
<condition field="destination_number" expression="^\d{5}$">
|
||||
<action application="log" data="INFO ************ redirecting ${destination_number} to ${destination_number}-DESKSHARE@video-mcu-stereo ***********" />
|
||||
<condition field="destination_number" expression="^\d{5}-DESKSHARE$">
|
||||
<action application="log" data="INFO ************WEBRTC DESKSHARE EXT***********" />
|
||||
<action application="answer"/>
|
||||
<action application="conference" data="${destination_number}-DESKSHARE@video-mcu-stereo"/>
|
||||
<action application="conference" data="${destination_number}@video-mcu-stereo"/>
|
||||
</condition>
|
||||
</extension>
|
||||
|
||||
|
@ -28,8 +28,6 @@ public class ESLEventListener implements IEslEventListener {
|
||||
private static final String STOP_RECORDING_EVENT = "stop-recording";
|
||||
|
||||
private static final String DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE";
|
||||
private static final String DESKSHARE_CALLER_NAME_SUFFIX = " (Screen)";
|
||||
private static final String DESKSHARE_CALLER_ID_SUFFIX = " (screen)";
|
||||
|
||||
private final ConferenceEventListener conferenceEventListener;
|
||||
|
||||
@ -68,53 +66,53 @@ public class ESLEventListener implements IEslEventListener {
|
||||
|
||||
String voiceUserId = callerIdName;
|
||||
|
||||
System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" + confName + "]");
|
||||
|
||||
Matcher gapMatcher = GLOBAL_AUDION_PATTERN.matcher(callerIdName);
|
||||
if (gapMatcher.matches()) {
|
||||
System.out.println("Ignoring GLOBAL AUDIO USER [" + callerIdName + "]");
|
||||
return;
|
||||
}
|
||||
|
||||
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
|
||||
// Deskstop sharing conferences have the user with the desktop video displayed in this way:
|
||||
// username (Screen) and usernum (screen)
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
|
||||
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
|
||||
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
|
||||
// (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
|
||||
// Voice conferences' name is of the form ddddd
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
|
||||
System.out.println("User joined deskshare conference, user=[" + callerIdName + "], " +
|
||||
"conf=[" + confName + "] callerId=[" + callerId + "]");
|
||||
DeskShareStartedEvent dsStart = new DeskShareStartedEvent(confName, callerId, callerIdName);
|
||||
conferenceEventListener.handleConferenceEvent(dsStart);
|
||||
}
|
||||
} else {
|
||||
Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
|
||||
if (matcher.matches()) {
|
||||
voiceUserId = matcher.group(1).trim();
|
||||
callerIdName = matcher.group(2).trim();
|
||||
}
|
||||
|
||||
Matcher matcher = CALLERNAME_PATTERN.matcher(callerIdName);
|
||||
if (matcher.matches()) {
|
||||
voiceUserId = matcher.group(1).trim();
|
||||
callerIdName = matcher.group(2).trim();
|
||||
}
|
||||
System.out.println("User joined voice conference, user=[" + callerIdName + "], conf=[" +
|
||||
confName + "] callerId=[" + callerId + "]");
|
||||
|
||||
VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
|
||||
conferenceEventListener.handleConferenceEvent(pj);
|
||||
VoiceUserJoinedEvent pj = new VoiceUserJoinedEvent(voiceUserId, memberId.toString(), confName, callerId, callerIdName, muted, speaking, "");
|
||||
conferenceEventListener.handleConferenceEvent(pj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void conferenceEventLeave(String uniqueId, String confName, int confSize, EslEvent event) {
|
||||
Integer memberId = this.getMemberIdFromEvent(event);
|
||||
System.out.println("User left voice conference, user=[" + memberId.toString() + "], conf=[" + confName + "]");
|
||||
String callerId = this.getCallerIdFromEvent(event);
|
||||
String callerIdName = this.getCallerIdNameFromEvent(event);
|
||||
|
||||
// Deskstop sharing conferences have their name in the form ddddd-DESKSHARE
|
||||
// Deskstop sharing conferences have the user with the desktop video displayed in this way:
|
||||
// username (Screen) and usernum (screen)
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX) &&
|
||||
callerId.endsWith(DESKSHARE_CALLER_ID_SUFFIX) &&
|
||||
callerIdName.endsWith(DESKSHARE_CALLER_NAME_SUFFIX)) {
|
||||
// (WebRTC) Deskstop sharing conferences' name is of the form ddddd-DESKSHARE
|
||||
// Voice conferences' name is of the form ddddd
|
||||
if (confName.endsWith(DESKSHARE_CONFERENCE_NAME_SUFFIX)) {
|
||||
System.out.println("User left deskshare conference, user=[" + memberId.toString() +
|
||||
"], " + "conf=[" + confName + "]");
|
||||
DeskShareEndedEvent dsEnd = new DeskShareEndedEvent(confName, callerId, callerIdName);
|
||||
conferenceEventListener.handleConferenceEvent(dsEnd);
|
||||
} else {
|
||||
System.out.println("User left voice conference, user=[" + memberId.toString() + "], " +
|
||||
"conf=[" + confName + "]");
|
||||
VoiceUserLeftEvent pl = new VoiceUserLeftEvent(memberId.toString(), confName);
|
||||
conferenceEventListener.handleConferenceEvent(pl);
|
||||
}
|
||||
|
||||
VoiceUserLeftEvent pl = new VoiceUserLeftEvent(memberId.toString(), confName);
|
||||
conferenceEventListener.handleConferenceEvent(pl);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -231,7 +229,8 @@ public class ESLEventListener implements IEslEventListener {
|
||||
|
||||
@Override
|
||||
public void eventReceived(EslEvent event) {
|
||||
System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]");
|
||||
System.out.println("ESL Event Listener received event=[" + event.getEventName() + "]" +
|
||||
event.getEventHeaders().toString());
|
||||
// if (event.getEventName().equals(FreeswitchHeartbeatMonitor.EVENT_HEARTBEAT)) {
|
||||
//// setChanged();
|
||||
// notifyObservers(event);
|
||||
|
@ -14,8 +14,7 @@ import org.bigbluebutton.common.messages.DeskShareRTMPBroadcastStoppedEventMessa
|
||||
|
||||
class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceService {
|
||||
|
||||
val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system";
|
||||
private final val DESKSHARE_CONFERENCE_NAME_SUFFIX = "-DESKSHARE"
|
||||
val FROM_VOICE_CONF_SYSTEM_CHAN = "bigbluebutton:from-voice-conf:system"
|
||||
|
||||
def voiceConfRecordingStarted(voiceConfId: String, recordStream: String, recording: java.lang.Boolean, timestamp: String) {
|
||||
val msg = new VoiceConfRecordingStartedMessage(voiceConfId, recordStream, recording, timestamp)
|
||||
@ -24,13 +23,14 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer
|
||||
|
||||
def userJoinedVoiceConf(voiceConfId: String, voiceUserId: String, userId: String, callerIdName: String,
|
||||
callerIdNum: String, muted: java.lang.Boolean, talking: java.lang.Boolean, avatarURL: String) {
|
||||
// println("******** FreeswitchConferenceService received voiceUserJoined vui=[" + userId + "] wui=[" + webUserId + "]")
|
||||
println("******** FreeswitchConferenceService received voiceUserJoined vui=[" +
|
||||
userId + "] wui=[" + voiceUserId + "]")
|
||||
val msg = new UserJoinedVoiceConfMessage(voiceConfId, voiceUserId, userId, callerIdName, callerIdNum, muted, talking, avatarURL)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def userLeftVoiceConf(voiceConfId: String, voiceUserId: String) {
|
||||
// println("******** FreeswitchConferenceService received voiceUserLeft vui=[" + userId + "] conference=[" + conference + "]")
|
||||
println("******** FreeswitchConferenceService received voiceUserLeft vui=[" + voiceUserId + "] conference=[" + voiceConfId + "]")
|
||||
val msg = new UserLeftVoiceConfMessage(voiceConfId, voiceUserId)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
@ -52,30 +52,26 @@ class VoiceConferenceService(sender: RedisPublisher) extends IVoiceConferenceSer
|
||||
}
|
||||
|
||||
def deskShareStarted(voiceConfId: String, callerIdNum: String, callerIdName: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareStarted to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareStartedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
|
||||
println("******** FreeswitchConferenceService send deskShareStarted to BBB " + voiceConfId)
|
||||
val msg = new DeskShareStartedEventMessage(voiceConfId, callerIdNum, callerIdName)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareEnded(voiceConfId: String, callerIdNum: String, callerIdName: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareStopped to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareStoppedEventMessage(trimmedVoiceConfId, callerIdNum, callerIdName)
|
||||
println("******** FreeswitchConferenceService send deskShareStopped to BBB " + voiceConfId)
|
||||
val msg = new DeskShareStoppedEventMessage(voiceConfId, callerIdNum, callerIdName)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStarted(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStartedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStarted to BBB " + voiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStartedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
def deskShareRTMPBroadcastStopped(voiceConfId: String, streamname: String, vw: java.lang.Integer, vh: java.lang.Integer, timestamp: String) {
|
||||
val trimmedVoiceConfId = voiceConfId.replace(DESKSHARE_CONFERENCE_NAME_SUFFIX, "")
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + trimmedVoiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStoppedEventMessage(trimmedVoiceConfId, streamname, vw, vh, timestamp)
|
||||
println("******** FreeswitchConferenceService send deskShareRTMPBroadcastStopped to BBB " + voiceConfId)
|
||||
val msg = new DeskShareRTMPBroadcastStoppedEventMessage(voiceConfId, streamname, vw, vh, timestamp)
|
||||
sender.publish(FROM_VOICE_CONF_SYSTEM_CHAN, msg.toJson())
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,66 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class AllowUserToShareDesktopReply implements ISubscribedMessage {
|
||||
public static final String NAME = "AllowUserToShareDesktopReply";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String MEETING_ID = "meeting_id";
|
||||
public static final String USER_ID = "user_id";
|
||||
public static final String ALLOWED = "allowed";
|
||||
|
||||
public final Long timestamp;
|
||||
public final String userId;
|
||||
public final String meetingId;
|
||||
public final Boolean allowed;
|
||||
|
||||
public AllowUserToShareDesktopReply(String meetingId, String userId,
|
||||
Boolean allowed, Long timestamp) {
|
||||
this.meetingId = meetingId;
|
||||
this.userId = userId;
|
||||
this.allowed = allowed;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(MEETING_ID, meetingId);
|
||||
payload.put(USER_ID, userId);
|
||||
payload.put(ALLOWED, allowed);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(NAME, VERSION, null);
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static AllowUserToShareDesktopReply fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (NAME.equals(messageName)) {
|
||||
|
||||
if (payload.has(TIMESTAMP) && payload.has(MEETING_ID)
|
||||
&& payload.has(USER_ID) && payload.has(ALLOWED)) {
|
||||
Long timestamp = payload.get(TIMESTAMP).getAsLong();
|
||||
String meetingId = payload.get(MEETING_ID).getAsString();
|
||||
String userId = payload.get(USER_ID).getAsString();
|
||||
Boolean allowed = payload.get(ALLOWED).getAsBoolean();
|
||||
return new AllowUserToShareDesktopReply(meetingId, userId, allowed,
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
public class AllowUserToShareDesktopRequest implements ISubscribedMessage {
|
||||
public static final String NAME = "AllowUserToShareDesktopRequest";
|
||||
public static final String VERSION = "0.0.1";
|
||||
|
||||
public static final String TIMESTAMP = "timestamp";
|
||||
public static final String MEETING_ID = "meeting_id";
|
||||
public static final String USER_ID = "user_id";
|
||||
|
||||
public final Long timestamp;
|
||||
public final String userId;
|
||||
public final String meetingId;
|
||||
|
||||
public AllowUserToShareDesktopRequest(String meetingId, String userId, Long timestamp) {
|
||||
this.meetingId = meetingId;
|
||||
this.userId = userId;
|
||||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(TIMESTAMP, timestamp);
|
||||
payload.put(MEETING_ID, meetingId);
|
||||
payload.put(USER_ID, userId);
|
||||
|
||||
java.util.HashMap<String, Object> header = MessageBuilder.buildHeader(NAME, VERSION, null);
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static AllowUserToShareDesktopRequest fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (NAME.equals(messageName)) {
|
||||
|
||||
if (payload.has(TIMESTAMP) && payload.has(MEETING_ID) && payload.has(USER_ID)) {
|
||||
Long timestamp = payload.get(TIMESTAMP).getAsLong();
|
||||
String meetingId = payload.get(MEETING_ID).getAsString();
|
||||
String userId = payload.get(USER_ID).getAsString();
|
||||
return new AllowUserToShareDesktopRequest(meetingId, userId, timestamp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MeetingCreatedMessage implements ISubscribedMessage {
|
||||
public static final String MEETING_CREATED = "meeting_created_message";
|
||||
public final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingId;
|
||||
public final Boolean record;
|
||||
|
||||
public MeetingCreatedMessage(String meetingId, Boolean record) {
|
||||
this.meetingId = meetingId;
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingId);
|
||||
|
||||
HashMap<String, Object> header = MessageBuilder.buildHeader(MEETING_CREATED, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static MeetingCreatedMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (MEETING_CREATED.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID) && payload.has(Constants.RECORDED)) {
|
||||
String meetingId = payload.get(Constants.MEETING_ID).getAsString();
|
||||
Boolean record = payload.get(Constants.RECORDED).getAsBoolean();
|
||||
return new MeetingCreatedMessage(meetingId, record);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package org.bigbluebutton.common.messages;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class MeetingDestroyedMessage implements ISubscribedMessage {
|
||||
public static final String NAME = "meeting_destroyed_event";
|
||||
public final String VERSION = "0.0.1";
|
||||
|
||||
public final String meetingId;
|
||||
|
||||
public MeetingDestroyedMessage(String meetingID) {
|
||||
this.meetingId = meetingID;
|
||||
}
|
||||
|
||||
public String toJson() {
|
||||
HashMap<String, Object> payload = new HashMap<String, Object>();
|
||||
payload.put(Constants.MEETING_ID, meetingId);
|
||||
|
||||
HashMap<String, Object> header = MessageBuilder.buildHeader(NAME, VERSION, null);
|
||||
|
||||
return MessageBuilder.buildJson(header, payload);
|
||||
}
|
||||
|
||||
public static MeetingDestroyedMessage fromJson(String message) {
|
||||
JsonParser parser = new JsonParser();
|
||||
JsonObject obj = (JsonObject) parser.parse(message);
|
||||
|
||||
if (obj.has("header") && obj.has("payload")) {
|
||||
JsonObject header = (JsonObject) obj.get("header");
|
||||
JsonObject payload = (JsonObject) obj.get("payload");
|
||||
|
||||
if (header.has("name")) {
|
||||
String messageName = header.get("name").getAsString();
|
||||
if (NAME.equals(messageName)) {
|
||||
if (payload.has(Constants.MEETING_ID)) {
|
||||
String meetingId = payload.get(Constants.MEETING_ID).getAsString();
|
||||
|
||||
return new MeetingDestroyedMessage(meetingId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
@ -22,12 +22,14 @@ import java.util.ArrayList;
|
||||
|
||||
public class BreakoutRoomsListPayload {
|
||||
|
||||
public final String meetingId;
|
||||
public final ArrayList<BreakoutRoomPayload> rooms;
|
||||
public final String meetingId;
|
||||
public final ArrayList<BreakoutRoomPayload> rooms;
|
||||
public final Boolean roomsReady;
|
||||
|
||||
public BreakoutRoomsListPayload(String meetingId,
|
||||
ArrayList<BreakoutRoomPayload> rooms) {
|
||||
this.meetingId = meetingId;
|
||||
this.rooms = rooms;
|
||||
}
|
||||
public BreakoutRoomsListPayload(String meetingId,
|
||||
ArrayList<BreakoutRoomPayload> rooms, Boolean roomsReady) {
|
||||
this.meetingId = meetingId;
|
||||
this.rooms = rooms;
|
||||
this.roomsReady = roomsReady;
|
||||
}
|
||||
}
|
||||
|
@ -9,11 +9,11 @@ public class CreateBreakoutRoomRequestPayload {
|
||||
public final String moderatorPassword;
|
||||
public final Integer durationInMinutes; // The duration of the breakout room
|
||||
public final String defaultPresentationURL;
|
||||
public final String recordType;
|
||||
public final Boolean record;
|
||||
|
||||
public CreateBreakoutRoomRequestPayload(String breakoutId, String parentId, String name,
|
||||
String voiceConfId, String viewerPassword, String moderatorPassword,
|
||||
Integer duration, String defaultPresentationURL, String recordType) {
|
||||
Integer duration, String defaultPresentationURL, Boolean record) {
|
||||
this.breakoutId = breakoutId;
|
||||
this.parentId = parentId;
|
||||
this.name = name;
|
||||
@ -22,6 +22,6 @@ public class CreateBreakoutRoomRequestPayload {
|
||||
this.moderatorPassword = moderatorPassword;
|
||||
this.durationInMinutes = duration;
|
||||
this.defaultPresentationURL = defaultPresentationURL;
|
||||
this.recordType = recordType;
|
||||
this.record = record;
|
||||
}
|
||||
}
|
||||
|
@ -10,14 +10,14 @@ public class CreateBreakoutRoomsRequestPayload {
|
||||
// The duration of the breakout room
|
||||
public final Integer durationInMinutes;
|
||||
// Breakout rooms recording option
|
||||
public final String recordType;
|
||||
public final Boolean record;
|
||||
|
||||
public CreateBreakoutRoomsRequestPayload(String meetingId,
|
||||
ArrayList<BreakoutRoomRequestPayload> breakoutRooms,
|
||||
Integer duration, String recordType) {
|
||||
Integer duration, Boolean record) {
|
||||
this.meetingId = meetingId;
|
||||
this.rooms = breakoutRooms;
|
||||
this.durationInMinutes = duration;
|
||||
this.recordType = recordType;
|
||||
this.record = record;
|
||||
}
|
||||
}
|
||||
|
@ -17,11 +17,11 @@ public class CreateBreakoutRoomRequestTest {
|
||||
String viewerPassword = "vp";
|
||||
String moderatorPassword = "mp";
|
||||
String defaultPresentationURL = "http://localhost/foo.pdf";
|
||||
String recordType = "none";
|
||||
Boolean record = false;
|
||||
|
||||
CreateBreakoutRoomRequestPayload payload =
|
||||
new CreateBreakoutRoomRequestPayload(breakoutId, parentId, name, voiceConfId,
|
||||
viewerPassword, moderatorPassword, durationInMinutes, defaultPresentationURL, recordType);
|
||||
viewerPassword, moderatorPassword, durationInMinutes, defaultPresentationURL, record);
|
||||
CreateBreakoutRoomRequest msg = new CreateBreakoutRoomRequest(payload);
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(msg);
|
||||
@ -37,6 +37,6 @@ public class CreateBreakoutRoomRequestTest {
|
||||
Assert.assertEquals(rxMsg.payload.moderatorPassword, moderatorPassword);
|
||||
Assert.assertEquals(rxMsg.payload.durationInMinutes, durationInMinutes);
|
||||
Assert.assertEquals(rxMsg.payload.defaultPresentationURL, defaultPresentationURL);
|
||||
Assert.assertEquals(rxMsg.payload.recordType, recordType);
|
||||
Assert.assertEquals(rxMsg.payload.record, record);
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ public class CreateBreakoutRoomsRequestTest {
|
||||
public void testCreateBreakoutRoomsRequest() {
|
||||
String meetingId = "abc123";
|
||||
Integer durationInMinutes = 20;
|
||||
String recordType = "moderator";
|
||||
Boolean record = true;
|
||||
|
||||
ArrayList<String> room1Users = new ArrayList<String>();
|
||||
room1Users.add("Tidora"); room1Users.add("Nidora"); room1Users.add("Tinidora");
|
||||
@ -31,7 +31,7 @@ public class CreateBreakoutRoomsRequestTest {
|
||||
ArrayList<BreakoutRoomRequestPayload> rooms = new ArrayList<BreakoutRoomRequestPayload>();
|
||||
rooms.add(room1); rooms.add(room2); rooms.add(room3);
|
||||
|
||||
CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, recordType);
|
||||
CreateBreakoutRoomsRequestPayload payload = new CreateBreakoutRoomsRequestPayload(meetingId, rooms, durationInMinutes, record);
|
||||
CreateBreakoutRoomsRequest msg = new CreateBreakoutRoomsRequest(payload);
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(msg);
|
||||
@ -43,6 +43,6 @@ public class CreateBreakoutRoomsRequestTest {
|
||||
Assert.assertEquals(rxMsg.payload.meetingId, meetingId);
|
||||
Assert.assertEquals(rxMsg.payload.rooms.size(), 3);
|
||||
Assert.assertEquals(rxMsg.payload.durationInMinutes, durationInMinutes);
|
||||
Assert.assertEquals(rxMsg.payload.recordType, recordType);
|
||||
Assert.assertEquals(rxMsg.payload.record, record);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
#Grails Metadata file
|
||||
#Thu Mar 20 10:48:08 PDT 2014
|
||||
app.grails.version=2.3.6
|
||||
#Fri Aug 19 19:12:11 UTC 2016
|
||||
app.grails.version=2.5.2
|
||||
app.name=lti
|
||||
app.version=0.2
|
||||
app.version=0.3
|
||||
|
@ -1,147 +0,0 @@
|
||||
<project xmlns:ivy="antlib:org.apache.ivy.ant" name="lti" default="test">
|
||||
<property environment="env"/>
|
||||
<property name="ivy.install.version" value="2.0.0" />
|
||||
<condition property="ivy.home" value="${env.IVY_HOME}">
|
||||
<isset property="env.IVY_HOME" />
|
||||
</condition>
|
||||
<property name="ivy.home" value="${user.home}/.ant" />
|
||||
<property name="ivy.jar.dir" value="${ivy.home}/lib" />
|
||||
<property name="ivy.jar.file" value="${ivy.jar.dir}/ivy-${ivy.install.version}.jar" />
|
||||
<property name="grails" value="grails" />
|
||||
|
||||
<target name="download-ivy" unless="offline">
|
||||
<available file="${ivy.jar.file}" property="ivy.available"/>
|
||||
<antcall target="-download-ivy" />
|
||||
</target>
|
||||
|
||||
<target name="-download-ivy" unless="ivy.available">
|
||||
<mkdir dir="${ivy.jar.dir}"/>
|
||||
<!-- download Ivy from web site so that it can be used even without any special installation -->
|
||||
<get src="http://archive.apache.org/dist/ant/ivy/${ivy.install.version}/apache-ivy-${ivy.install.version}-bin.zip"
|
||||
dest="${ivy.home}/ivy.zip" usetimestamp="true" verbose="true"/>
|
||||
<unzip src="${ivy.home}/ivy.zip" dest="${ivy.jar.dir}">
|
||||
<patternset>
|
||||
<include name="**/*.jar"/>
|
||||
</patternset>
|
||||
<mapper type="flatten"/>
|
||||
</unzip>
|
||||
</target>
|
||||
|
||||
<target name="init-ivy" depends="download-ivy" unless="ivy.lib.path">
|
||||
<!-- try to load ivy here from ivy home, in case the user has not already dropped
|
||||
it into ant's lib dir (note that the latter copy will always take precedence).
|
||||
We will not fail as long as local lib dir exists (it may be empty) and
|
||||
ivy is in at least one of ant's lib dir or the local lib dir. -->
|
||||
<path id="ivy.lib.path">
|
||||
<fileset dir="${ivy.jar.dir}" includes="*.jar"/>
|
||||
</path>
|
||||
<taskdef resource="org/apache/ivy/ant/antlib.xml"
|
||||
uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
|
||||
</target>
|
||||
|
||||
|
||||
<property name="lib.dir" value="${basedir}/lib"/>
|
||||
|
||||
<macrodef name="grails">
|
||||
<attribute name="script"/>
|
||||
<attribute name="args" default="" />
|
||||
<sequential>
|
||||
<grailsTask script="@{script}" args="@{args}" classpathref="grails.classpath">
|
||||
<compileClasspath refid="compile.classpath"/>
|
||||
<testClasspath refid="test.classpath"/>
|
||||
<runtimeClasspath refid="app.classpath"/>
|
||||
</grailsTask>
|
||||
</sequential>
|
||||
</macrodef>
|
||||
|
||||
<!-- =================================
|
||||
target: resolve
|
||||
================================= -->
|
||||
<target name="-resolve" description="--> Retrieve dependencies with ivy" depends="init-ivy">
|
||||
<ivy:retrieve pattern="${lib.dir}/[conf]/[artifact]-[revision].[ext]"/>
|
||||
</target>
|
||||
|
||||
<target name="-init-grails" depends="-resolve">
|
||||
<path id="grails.classpath">
|
||||
<fileset dir="${lib.dir}/build"/>
|
||||
<fileset dir="${lib.dir}"/>
|
||||
</path>
|
||||
|
||||
<path id="compile.classpath">
|
||||
<fileset dir="${lib.dir}/compile"/>
|
||||
</path>
|
||||
|
||||
<path id="test.classpath">
|
||||
<fileset dir="${lib.dir}/test"/>
|
||||
</path>
|
||||
|
||||
<path id="app.classpath">
|
||||
<fileset dir="${lib.dir}/runtime"/>
|
||||
</path>
|
||||
|
||||
<taskdef name="grailsTask"
|
||||
classname="grails.ant.GrailsTask"
|
||||
classpathref="grails.classpath"/>
|
||||
</target>
|
||||
|
||||
<target name="deps-report" depends="-resolve" description="--> Generate report of module dependencies.">
|
||||
<ivy:report conf="*"/>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: clean
|
||||
================================= -->
|
||||
<target name="clean" description="--> Cleans a Grails application">
|
||||
<delete failonerror="true">
|
||||
<fileset dir="${lib.dir}/build" includes="*/"/>
|
||||
<fileset dir="${lib.dir}/compile" includes="*/"/>
|
||||
<fileset dir="${lib.dir}/runtime" includes="*/"/>
|
||||
<fileset dir="${lib.dir}/test" includes="*/"/>
|
||||
</delete>
|
||||
<antcall target="--grails-clean"/>
|
||||
</target>
|
||||
|
||||
<!-- extra target to avoid errors on Windows because libs on classpath can not be deleted -->
|
||||
<target name="--grails-clean" depends="-init-grails">
|
||||
<grails script="Clean"/>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: compile
|
||||
================================= -->
|
||||
<target name="compile" depends="-init-grails" description="--> Compiles a Grails application">
|
||||
<grails script="Compile"/>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: war
|
||||
================================= -->
|
||||
|
||||
<target name="war" depends="-init-grails" description="--> Creates a WAR of a Grails application">
|
||||
<exec executable="${grails}" failonerror="true">
|
||||
<arg value="war"/>
|
||||
</exec>
|
||||
<!--grails script="War"/-->
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: test
|
||||
================================= -->
|
||||
<target name="test" depends="-init-grails" description="--> Run a Grails applications unit tests">
|
||||
<grails script="TestApp"/>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: run
|
||||
================================= -->
|
||||
<target name="run" depends="-init-grails" description="--> Runs a Grails application using embedded Jetty">
|
||||
<grails script="RunApp"/>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: deploy
|
||||
================================= -->
|
||||
<target name="deploy" depends="war" description="--> The deploy target (initially empty)">
|
||||
<!-- TODO -->
|
||||
</target>
|
||||
</project>
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 9.8 KiB After Width: | Height: | Size: 9.8 KiB |
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 65 B After Width: | Height: | Size: 65 B |
Before Width: | Height: | Size: 400 B After Width: | Height: | Size: 400 B |
BIN
bbb-lti/grails-app/assets/images/favicon.ico
Normal file
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 9.9 KiB After Width: | Height: | Size: 9.9 KiB |
Before Width: | Height: | Size: 80 B After Width: | Height: | Size: 80 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
BIN
bbb-lti/grails-app/assets/images/icon.ico
Normal file
After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 67 B After Width: | Height: | Size: 67 B |
Before Width: | Height: | Size: 658 B After Width: | Height: | Size: 658 B |
Before Width: | Height: | Size: 659 B After Width: | Height: | Size: 659 B |
Before Width: | Height: | Size: 767 B After Width: | Height: | Size: 767 B |
Before Width: | Height: | Size: 755 B After Width: | Height: | Size: 755 B |
Before Width: | Height: | Size: 726 B After Width: | Height: | Size: 726 B |
Before Width: | Height: | Size: 701 B After Width: | Height: | Size: 701 B |
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 806 B |
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 778 B |
Before Width: | Height: | Size: 300 B After Width: | Height: | Size: 300 B |
Before Width: | Height: | Size: 835 B After Width: | Height: | Size: 835 B |
Before Width: | Height: | Size: 834 B After Width: | Height: | Size: 834 B |
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 652 B |
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
13
bbb-lti/grails-app/assets/stylesheets/application.css
Normal file
@ -0,0 +1,13 @@
|
||||
/*
|
||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||
* listed below.
|
||||
*
|
||||
* Any CSS file within this directory can be referenced here using a relative path.
|
||||
*
|
||||
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
||||
* compiled file, but it's generally better to create a new file per style scope.
|
||||
*
|
||||
*= require main
|
||||
*= require mobile
|
||||
*= require_self
|
||||
*/
|
@ -48,7 +48,7 @@ body {
|
||||
}
|
||||
|
||||
#grailsLogo {
|
||||
background-color: #30406b;
|
||||
background-color: #abbf78;
|
||||
}
|
||||
|
||||
/* replace with .no-boxshadow body if you have modernizr available */
|
||||
@ -108,7 +108,7 @@ img {
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: #30406b;
|
||||
background: #abbf78;
|
||||
color: #000;
|
||||
clear: both;
|
||||
font-size: 0.8em;
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
modules = {
|
||||
application {
|
||||
resource url:'js/application.js'
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
class BootStrap {
|
||||
|
||||
def init = { servletContext ->
|
||||
|
@ -16,7 +16,7 @@
|
||||
with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
grails.servlet.version = "3.0"
|
||||
grails.servlet.version = "3.0" // Change depending on target container compliance (2.5 or 3.0)
|
||||
grails.project.class.dir = "target/classes"
|
||||
grails.project.test.class.dir = "target/test-classes"
|
||||
grails.project.test.reports.dir = "target/test-reports"
|
||||
@ -26,48 +26,64 @@ grails.project.source.level = 1.6
|
||||
//grails.project.war.file = "target/${appName}-${appVersion}.war"
|
||||
|
||||
grails.project.fork = [
|
||||
// configure settings for compilation JVM, note that if you alter the Groovy version forked compilation is required
|
||||
// compile: [maxMemory: 256, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
|
||||
|
||||
// configure settings for the test-app JVM, uses the daemon by default
|
||||
test: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, daemon:true],
|
||||
// configure settings for the run-app JVM
|
||||
run: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
|
||||
// configure settings for the run-war JVM
|
||||
war: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256, forkReserve:false],
|
||||
// configure settings for the Console UI JVM
|
||||
console: [maxMemory: 768, minMemory: 64, debug: false, maxPerm: 256]
|
||||
]
|
||||
|
||||
grails.project.dependency.resolver = "maven" // or ivy
|
||||
grails.project.dependency.resolution = {
|
||||
// inherit Grails' default dependencies
|
||||
inherits("global") {
|
||||
// specify dependency exclusions here; for example, uncomment this to disable ehcache:
|
||||
// excludes 'ehcache'
|
||||
}
|
||||
log "error"
|
||||
checksums true
|
||||
legacyResolve false
|
||||
log "error" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
|
||||
checksums true // Whether to verify checksums on resolve
|
||||
legacyResolve false // whether to do a secondary resolve on plugin installation, not advised and here for backwards compatibility
|
||||
|
||||
repositories {
|
||||
inherits true
|
||||
inherits true // Whether to inherit repository definitions from plugins
|
||||
|
||||
grailsPlugins()
|
||||
grailsHome()
|
||||
mavenLocal()
|
||||
grailsCentral()
|
||||
mavenCentral()
|
||||
|
||||
mavenRepo "http://snapshots.repository.codehaus.org"
|
||||
// uncomment these (or add new ones) to enable remote dependency resolution from public Maven repositories
|
||||
mavenRepo "http://repository.codehaus.org"
|
||||
mavenRepo "http://download.java.net/maven/2/"
|
||||
mavenRepo "http://repository.jboss.com/maven2/"
|
||||
//mavenRepo "https://raw.github.com/blindsidenetworks/oauth/mvn-repo/"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
//runtime "commons-net:commons-net:3.0.1"
|
||||
//runtime "net.oauth:oauth:1.0.1"
|
||||
// specify dependencies here under either 'build', 'compile', 'runtime', 'test' or 'provided' scopes e.g.
|
||||
// runtime 'mysql:mysql-connector-java:5.1.29'
|
||||
// runtime 'org.postgresql:postgresql:9.3-1101-jdbc41'
|
||||
test "org.grails:grails-datastore-test-support:1.0.2-grails-2.4"
|
||||
}
|
||||
|
||||
plugins {
|
||||
// plugins for the build system only
|
||||
build ":tomcat:7.0.50.1"
|
||||
build ":tomcat:7.0.55.3" // or ":tomcat:8.0.22"
|
||||
|
||||
runtime ":database-migration:1.3.8"
|
||||
runtime ":jquery:1.11.0"
|
||||
runtime ":resources:1.2.1"
|
||||
runtime ':twitter-bootstrap:3.1.1'
|
||||
// plugins for the compile step
|
||||
compile ":scaffolding:2.1.2"
|
||||
compile ':cache:1.1.8'
|
||||
// asset-pipeline 2.0+ requires Java 7, use version 1.9.x with Java 6
|
||||
compile ":asset-pipeline:2.5.7"
|
||||
|
||||
// plugins needed at runtime but not for compilation
|
||||
runtime ":database-migration:1.4.0"
|
||||
runtime ":jquery:1.11.1"
|
||||
runtime ':twitter-bootstrap:3.3.4'
|
||||
}
|
||||
}
|
||||
|
@ -20,11 +20,10 @@
|
||||
// config files can be ConfigSlurper scripts, Java properties files, or classes
|
||||
// in the classpath in ConfigSlurper format
|
||||
|
||||
grails.config.locations = [ "classpath:lti.properties"]
|
||||
// grails.config.locations = [ "classpath:${appName}-config.properties",
|
||||
// "classpath:${appName}-config.groovy",
|
||||
// "file:${userHome}/.grails/${appName}-config.properties",
|
||||
// "file:${userHome}/.grails/${appName}-config.groovy"]
|
||||
grails.config.locations = [ "classpath:${appName}-config.properties",
|
||||
"classpath:${appName}-config.groovy",
|
||||
"file:${userHome}/.grails/${appName}-config.properties",
|
||||
"file:${userHome}/.grails/${appName}-config.groovy"]
|
||||
|
||||
// if (System.properties["${appName}.config.location"]) {
|
||||
// grails.config.locations << "file:" + System.properties["${appName}.config.location"]
|
||||
@ -32,8 +31,6 @@ grails.config.locations = [ "classpath:lti.properties"]
|
||||
|
||||
grails.project.groupId = appName // change this to alter the default package name and Maven publishing destination
|
||||
|
||||
grails.mime.file.extensions = true // enables the parsing of file extensions from URLs into the request format
|
||||
grails.mime.use.accept.header = false
|
||||
// The ACCEPT header will not be used for content negotiation for user agents containing the following strings (defaults to the 4 major rendering engines)
|
||||
grails.mime.disable.accept.header.userAgents = ['Gecko', 'WebKit', 'Presto', 'Trident']
|
||||
grails.mime.types = [ // the first one is the default format
|
||||
@ -55,10 +52,6 @@ grails.mime.types = [ // the first one is the default format
|
||||
// URL Mapping Cache Max Size, defaults to 5000
|
||||
//grails.urlmapping.cache.maxsize = 1000
|
||||
|
||||
// What URL patterns should be processed by the resources plugin
|
||||
grails.resources.adhoc.patterns = ['/images/*', '/css/*', '/js/*', '/plugins/*']
|
||||
grails.resources.adhoc.excludes = ['/WEB-INF/**']
|
||||
|
||||
// Legacy setting for codec used to encode data with ${}
|
||||
grails.views.default.codec = "html"
|
||||
|
||||
@ -104,6 +97,12 @@ grails.exceptionresolver.params.exclude = ['password']
|
||||
// configure auto-caching of queries by default (if false you can cache individual queries with 'cache: true')
|
||||
grails.hibernate.cache.queries = false
|
||||
|
||||
// configure passing transaction's read-only attribute to Hibernate session, queries and criterias
|
||||
// set "singleSession = false" OSIV mode in hibernate configuration after enabling
|
||||
grails.hibernate.pass.readonly = false
|
||||
// configure passing read-only to OSIV session by default, requires "singleSession = false" OSIV mode
|
||||
grails.hibernate.osiv.readonly = false
|
||||
|
||||
environments {
|
||||
development {
|
||||
grails.logging.jul.usebridge = true
|
||||
@ -114,7 +113,7 @@ environments {
|
||||
}
|
||||
|
||||
// log4j configuration
|
||||
log4j = {
|
||||
log4j.main = {
|
||||
appenders {
|
||||
rollingFile name:"logfile", maxFileSize:1000000, file:"/var/log/bigbluebutton/bbb-lti.log", layout:pattern(conversionPattern: '%d{[dd.MM.yy HH:mm:ss.SSS]} %-5p %c %x - %m%n')
|
||||
console name:'console', layout:pattern(conversionPattern: '%d{[dd.MM.yy HH:mm:ss.SSS]} %-5p %c %x - %m%n')
|
||||
@ -133,7 +132,4 @@ log4j = {
|
||||
'org.springframework',
|
||||
'org.hibernate',
|
||||
'net.sf.ehcache.hibernate'
|
||||
|
||||
warn 'org.mortbay.log'
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
*/
|
||||
class UrlMappings {
|
||||
|
||||
static mappings = {
|
||||
static mappings = {
|
||||
"/$controller/$action?/$id?(.$format)?"{
|
||||
constraints {
|
||||
// apply constraints here
|
||||
@ -26,5 +26,5 @@ class UrlMappings {
|
||||
|
||||
"/"(controller: "tool")
|
||||
"500"(view:'/error')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -382,8 +382,8 @@ class ToolController {
|
||||
def lti_endpoint = ltiService.retrieveBasicLtiEndpoint() + '/' + grailsApplication.metadata['app.name']
|
||||
def launch_url = 'http://' + lti_endpoint + '/tool'
|
||||
def secure_launch_url = 'https://' + lti_endpoint + '/tool'
|
||||
def icon = 'http://' + lti_endpoint + '/images/icon.ico'
|
||||
def secure_icon = 'https://' + lti_endpoint + '/images/icon.ico'
|
||||
def icon = 'http://' + lti_endpoint + '/assets/icon.ico'
|
||||
def secure_icon = 'https://' + lti_endpoint + '/assets/icon.ico'
|
||||
def isSSLEnabled = ltiService.isSSLEnabled('https://' + lti_endpoint + '/tool/test')
|
||||
def cartridge = '' +
|
||||
'<?xml version="1.0" encoding="UTF-8"?>' +
|
||||
|
@ -198,7 +198,7 @@ class BigbluebuttonService {
|
||||
private String getCreateURL(String name, String meetingID, String attendeePW, String moderatorPW, String welcome, Integer voiceBridge, String logoutURL, String record, Integer duration, String meta ) {
|
||||
voiceBridge = ( voiceBridge == null || voiceBridge == 0 )? 70000 + new Random(System.currentTimeMillis()).nextInt(10000): voiceBridge;
|
||||
|
||||
String url = bbbProxy.getCreateURL(name, meetingID, attendeePW, moderatorPW, welcome, "", voiceBridge.toString(), "", logoutURL, "", record, duration.toString(), meta );
|
||||
String url = bbbProxy.getCreateURL(name, meetingID, attendeePW, moderatorPW, welcome, null, voiceBridge.toString(), null, logoutURL, null, record, duration.toString(), meta );
|
||||
return url;
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<title><g:if env="development">Grails Runtime Exception</g:if><g:else>Error</g:else></title>
|
||||
<meta name="layout" content="main">
|
||||
<g:if env="development"><link rel="stylesheet" href="${resource(dir: 'css', file: 'errors.css')}" type="text/css"></g:if>
|
||||
<g:if env="development"><asset:stylesheet src="errors.css"/></g:if>
|
||||
</head>
|
||||
<body>
|
||||
<g:if env="development">
|
||||
|
@ -9,24 +9,24 @@
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title><g:message code="tool.view.title" /></title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="${resource(dir: 'images', file: 'favicon.ico')}" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" href="${resource(dir: 'images', file: 'apple-touch-icon.png')}">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="${resource(dir: 'images', file: 'apple-touch-icon-retina.png')}">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'main.css')}" type="text/css">
|
||||
<link rel="stylesheet" href="${resource(dir: 'css', file: 'mobile.css')}" type="text/css">
|
||||
<link rel="shortcut icon" href="${assetPath(src: 'favicon.ico')}" type="image/x-icon">
|
||||
<link rel="apple-touch-icon" href="${assetPath(src: 'apple-touch-icon.png')}">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="${assetPath(src: 'apple-touch-icon-retina.png')}">
|
||||
<asset:stylesheet src="main.css"/>
|
||||
<asset:stylesheet src="mobile.css"/>
|
||||
<asset:stylesheet src="application.css"/>
|
||||
<asset:javascript src="application.js"/>
|
||||
<g:layoutHead/>
|
||||
<g:javascript library="application"/>
|
||||
<r:layoutResources />
|
||||
</head>
|
||||
<body>
|
||||
<div id="spinner" class="spinner" style="display:none;">
|
||||
<img src="${resource(dir:'images',file:'spinner.gif')}" alt="Spinner" />
|
||||
</div>
|
||||
<div class="logo" id="logo" role="banner"><img src="${resource(dir: 'images', file: 'bbb_logo.jpg')}" alt="<g:message code="tool.view.app" />" /></div>
|
||||
<g:layoutBody />
|
||||
|
||||
<div id="spinner" class="spinner" style="display:none;">
|
||||
<asset:image src="spinner.gif" alt="Spinner"/>
|
||||
</div>
|
||||
<div class="logo" id="logo" role="banner">
|
||||
<a href="http://bigbluebutton.org"><asset:image src="bbb_logo.jpg" alt="BigBlueButton"/></a>
|
||||
</div>
|
||||
<g:layoutBody />
|
||||
<div class="footer" role="contentinfo"></div>
|
||||
<div id="spinner" class="spinner" style="display:none;"><g:message code="spinner.alt" default="Loading…"/></div>
|
||||
<r:layoutResources />
|
||||
</body>
|
||||
</html>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
|
||||
<meta name="layout" content="main"/>
|
||||
<title>Insert title here</title>
|
||||
<title>Error</title>
|
||||
</head>
|
||||
<body>
|
||||
<div class="body">
|
||||
|
@ -1,8 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><g:message code="tool.view.title" /></title>
|
||||
<link rel="stylesheet" href="${resource(dir:'css',file:'bootstrap.css')}" />
|
||||
<link rel="shortcut icon" href="${resource(dir:'images',file:'favicon.ico')}" type="image/x-icon" />
|
||||
<link rel="shortcut icon" href="${assetPath(src: 'favicon.ico')}" type="image/x-icon">
|
||||
<asset:stylesheet src="bootstrap.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<!-- tool.index -->
|
||||
|
363
bbb-lti/grailsw
Normal file
@ -0,0 +1,363 @@
|
||||
##############################################################################
|
||||
## ##
|
||||
## Grails JVM Bootstrap for UN*X ##
|
||||
## ##
|
||||
##############################################################################
|
||||
|
||||
PROGNAME=`basename "$0"`
|
||||
DIRNAME=`dirname "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn() {
|
||||
echo "${PROGNAME}: $*"
|
||||
}
|
||||
|
||||
die() {
|
||||
warn "$*"
|
||||
exit 1
|
||||
}
|
||||
|
||||
earlyInit() {
|
||||
return
|
||||
}
|
||||
lateInit() {
|
||||
return
|
||||
}
|
||||
|
||||
GROOVY_STARTUP=~/.groovy/startup
|
||||
if [ -r "$GROOVY_STARTUP" ]; then
|
||||
. "$GROOVY_STARTUP"
|
||||
fi
|
||||
|
||||
earlyInit
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false;
|
||||
case "`uname`" in
|
||||
CYGWIN*)
|
||||
cygwin=true
|
||||
;;
|
||||
|
||||
Darwin*)
|
||||
darwin=true
|
||||
;;
|
||||
|
||||
MINGW*)
|
||||
mingw=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set JAVA_HOME if it's not already set
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
|
||||
# Set JAVA_HOME for Darwin
|
||||
if $darwin; then
|
||||
|
||||
[ -z "$JAVA_HOME" -a -d "/Library/Java/Home" ] &&
|
||||
export JAVA_HOME="/Library/Java/Home"
|
||||
|
||||
[ -z "$JAVA_HOME" -a -d "/System/Library/Frameworks/JavaVM.framework/Home" ] &&
|
||||
export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
|
||||
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$GRAILS_HOME" ] &&
|
||||
GRAILS_HOME=`cygpath --unix "$GRAILS_HOME"`
|
||||
[ -n "$JAVACMD" ] &&
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CP" ] &&
|
||||
CP=`cygpath --path --unix "$CP"`
|
||||
fi
|
||||
|
||||
# quick detection of JDK version without JVM startup overhead
|
||||
CLASSFILE_HEADER_FILE="${JAVA_HOME}/include/classfile_constants.h"
|
||||
if [ -z "$GRAILS_NO_PERMGEN" -a -f "${CLASSFILE_HEADER_FILE}" ]; then
|
||||
CLASSFILE_VERSION=`cat "${CLASSFILE_HEADER_FILE}" | grep "#define JVM_CLASSFILE_MAJOR_VERSION" | awk '{ print $3 }' 2> /dev/null`
|
||||
if [ 0$CLASSFILE_VERSION -ge 52 ]; then
|
||||
GRAILS_NO_PERMGEN=1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Remove possible trailing slash (after possible cygwin correction)
|
||||
GRAILS_HOME=`echo $GRAILS_HOME | sed -e 's|/$||g'`
|
||||
|
||||
# Locate GRAILS_HOME if not it is not set
|
||||
if [ -z "$GRAILS_HOME" -o ! -d "$GRAILS_HOME" ] ; then
|
||||
# resolve links - $0 may be a link to groovy's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/.."
|
||||
GRAILS_HOME="`pwd -P`"
|
||||
cd "$SAVED"
|
||||
fi
|
||||
|
||||
# Warn the user if JAVA_HOME and/or GRAILS_HOME are not set.
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
die "JAVA_HOME environment variable is not set"
|
||||
elif [ ! -d "$JAVA_HOME" ] ; then
|
||||
die "JAVA_HOME is not a directory: $JAVA_HOME"
|
||||
fi
|
||||
|
||||
if [ -z "$GRAILS_HOME" ] ; then
|
||||
warn "GRAILS_HOME environment variable is not set"
|
||||
fi
|
||||
|
||||
if [ ! -d "$GRAILS_HOME" ] ; then
|
||||
die "GRAILS_HOME is not a directory: $GRAILS_HOME"
|
||||
fi
|
||||
|
||||
# Use default groovy-conf config
|
||||
if [ -z "$STARTER_CONF" ]; then
|
||||
STARTER_CONF="$GRAILS_HOME/conf/groovy-starter.conf"
|
||||
fi
|
||||
STARTER_CLASSPATH="wrapper/grails-wrapper-runtime-2.5.2.jar:wrapper:."
|
||||
|
||||
# Allow access to Cocoa classes on OS X
|
||||
if $darwin; then
|
||||
STARTER_CLASSPATH="$STARTER_CLASSPATH:/System/Library/Java/Support"
|
||||
fi
|
||||
|
||||
# Create the final classpath
|
||||
# Setting a classpath using the -cp or -classpath option means not to use
|
||||
# the global classpath. Groovy behaves then the same as the java
|
||||
# interpreter
|
||||
if [ -n "$CP" ] ; then
|
||||
CP="$CP"
|
||||
elif [ -n "$CLASSPATH" ] ; then
|
||||
CP="$CLASSPATH"
|
||||
fi
|
||||
|
||||
# Determine the Java command to use to start the JVM
|
||||
if [ -z "$JAVACMD" ]; then
|
||||
if [ -n "$JAVA_HOME" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
fi
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ]; then
|
||||
die "JAVA_HOME is not defined correctly; can not execute: $JAVACMD"
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can
|
||||
if [ "$cygwin" = "false" ]; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ "$MAX_FD_LIMIT" != "unlimited" ]; then
|
||||
if [ $? -eq 0 ]; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ]; then
|
||||
# use the businessSystem max
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ]; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query businessSystem maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Fix the cygwin agent issue
|
||||
AGENT_GRAILS_HOME=$GRAILS_HOME
|
||||
if $cygwin ; then
|
||||
[ -n "$GRAILS_HOME" ] &&
|
||||
AGENT_GRAILS_HOME=`cygpath --windows "$GRAILS_HOME"`
|
||||
fi
|
||||
|
||||
if $mingw ; then
|
||||
# Converts GRAILS_HOME path to Windows syntax
|
||||
[ -n "$GRAILS_HOME" ] &&
|
||||
AGENT_GRAILS_HOME=`cmd //C echo "$GRAILS_HOME"`
|
||||
fi
|
||||
|
||||
if [ -z "$GRAILS_AGENT_CACHE_DIR" ]; then
|
||||
GRAILS_AGENT_CACHE_DIR=~/.grails/2.5.2/
|
||||
fi
|
||||
SPRINGLOADED_PARAMS=profile=grails\;cacheDir=$GRAILS_AGENT_CACHE_DIR
|
||||
if [ ! -d "$GRAILS_AGENT_CACHE_DIR" ]; then
|
||||
mkdir -p "$GRAILS_AGENT_CACHE_DIR"
|
||||
fi
|
||||
|
||||
# Process JVM args
|
||||
AGENT_STRING="-javaagent:wrapper/springloaded-1.2.4.RELEASE.jar -Xverify:none -Dspringloaded.synchronize=true -Djdk.reflect.allowGetCallerClass=true -Dspringloaded=$SPRINGLOADED_PARAMS"
|
||||
CMD_LINE_ARGS=""
|
||||
DISABLE_RELOADING=true
|
||||
|
||||
while true; do
|
||||
if [ "$1" = "-cp" ] || [ "$1" = "-classpath" ]; then
|
||||
CP=$2
|
||||
shift 2
|
||||
break
|
||||
fi
|
||||
|
||||
if [ "$1" = "-reloading" ]; then
|
||||
AGENT=$AGENT_STRING
|
||||
DISABLE_RELOADING=false
|
||||
shift
|
||||
break
|
||||
fi
|
||||
|
||||
if [ "$1" = "-noreloading" ]; then
|
||||
DISABLE_RELOADING=true
|
||||
shift
|
||||
break
|
||||
fi
|
||||
|
||||
if [ "$1" = "-debug" ]; then
|
||||
JAVA_OPTS="$JAVA_OPTS -Xdebug -Xnoagent -Dgrails.full.stacktrace=true -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
|
||||
shift
|
||||
break
|
||||
fi
|
||||
|
||||
if [ "$1" != -* ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
CMD_LINE_ARGS="$CMD_LINE_ARGS $1"
|
||||
shift
|
||||
done
|
||||
|
||||
# Enable agent-based reloading for the 'run-app' command.
|
||||
if ! $DISABLE_RELOADING; then
|
||||
for a in "$@"; do
|
||||
if [ "$a" = "run-app" ]; then
|
||||
AGENT=$AGENT_STRING
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $# = 0 ]; then
|
||||
AGENT=$AGENT_STRING
|
||||
fi
|
||||
fi
|
||||
|
||||
ARGUMENTS="$CMD_LINE_ARGS $@"
|
||||
|
||||
# Setup Profiler
|
||||
useprofiler=false
|
||||
if [ "x$PROFILER" != "x" ]; then
|
||||
if [ -r "$PROFILER" ]; then
|
||||
. $PROFILER
|
||||
useprofiler=true
|
||||
else
|
||||
die "Profiler file not found: $PROFILER"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, use classes.jar for TOOLS_JAR
|
||||
TOOLS_JAR="$JAVA_HOME/lib/tools.jar"
|
||||
if $darwin; then
|
||||
JAVA_OPTS="-Xdock:name=Grails -Xdock:icon=$GRAILS_HOME/media/icons/grails.icns $JAVA_OPTS"
|
||||
# TOOLS_JAR="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar"
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
GRAILS_HOME=`cygpath --path --mixed "$GRAILS_HOME"`
|
||||
JAVA_HOME=`cygpath --path --mixed "$JAVA_HOME"`
|
||||
STARTER_CONF=`cygpath --path --mixed "$STARTER_CONF"`
|
||||
if [ "x$CP" != "x" ] ; then
|
||||
CP=`cygpath --path --mixed "$CP"`
|
||||
fi
|
||||
TOOLS_JAR=`cygpath --path --mixed "$TOOLS_JAR"`
|
||||
STARTER_CLASSPATH=`cygpath --path --mixed "$STARTER_CLASSPATH"`
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GROOVY_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GROOVY_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments
|
||||
ARGUMENTS=""
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
if [ $CHECK -ne 0 ] ; then
|
||||
convArg=`cygpath --path --ignore --mixed -- "$arg"`
|
||||
else
|
||||
convArg=$arg
|
||||
fi
|
||||
ARGUMENTS="$ARGUMENTS $convArg"
|
||||
done
|
||||
fi
|
||||
|
||||
STARTER_MAIN_CLASS=org.grails.wrapper.GrailsWrapper
|
||||
|
||||
lateInit
|
||||
|
||||
startGrails() {
|
||||
CLASS=$1
|
||||
shift
|
||||
if [ -n "$GRAILS_OPTS" ]
|
||||
then
|
||||
GRAILS_OPTS="$GRAILS_OPTS"
|
||||
else
|
||||
GRAILS_OPTS="-server -Xmx768M -Xms64M -Dfile.encoding=UTF-8"
|
||||
if [ "$GRAILS_NO_PERMGEN" != "1" ]; then
|
||||
GRAILS_OPTS="$GRAILS_OPTS -XX:PermSize=32m -XX:MaxPermSize=256m"
|
||||
fi
|
||||
fi
|
||||
JAVA_OPTS="$GRAILS_OPTS $JAVA_OPTS $AGENT"
|
||||
# Start the Profiler or the JVM
|
||||
if $useprofiler; then
|
||||
runProfiler
|
||||
else
|
||||
if [ $# -eq 0 ] ; then # no argument given
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
-classpath "$STARTER_CLASSPATH" \
|
||||
-Dgrails.home="$GRAILS_HOME" \
|
||||
-Dtools.jar="$TOOLS_JAR" \
|
||||
-Djava.net.preferIPv4Stack=true \
|
||||
$STARTER_MAIN_CLASS \
|
||||
--main $CLASS \
|
||||
--conf "$STARTER_CONF" \
|
||||
--classpath "$CP"
|
||||
else
|
||||
exec "$JAVACMD" $JAVA_OPTS \
|
||||
-classpath "$STARTER_CLASSPATH" \
|
||||
-Dgrails.home="$GRAILS_HOME" \
|
||||
-Dtools.jar="$TOOLS_JAR" \
|
||||
-Djava.net.preferIPv4Stack=true \
|
||||
$STARTER_MAIN_CLASS \
|
||||
--main $CLASS \
|
||||
--conf "$STARTER_CONF" \
|
||||
--classpath "$CP" \
|
||||
"${ARGUMENTS}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
startGrails $STARTER_MAIN_CLASS "$@"
|
186
bbb-lti/grailsw.bat
Normal file
@ -0,0 +1,186 @@
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem ##
|
||||
@rem Grails JVM Bootstrap for Windows ##
|
||||
@rem ##
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set CLASS=org.grails.wrapper.GrailsWrapper
|
||||
|
||||
if exist "%USERPROFILE%/.groovy/preinit.bat" call "%USERPROFILE%/.groovy/preinit.bat"
|
||||
|
||||
@rem Determine the command interpreter to execute the "CD" later
|
||||
set COMMAND_COM="cmd.exe"
|
||||
if exist "%SystemRoot%\system32\cmd.exe" set COMMAND_COM="%SystemRoot%\system32\cmd.exe"
|
||||
if exist "%SystemRoot%\command.com" set COMMAND_COM="%SystemRoot%\command.com"
|
||||
|
||||
@rem Use explicit find.exe to prevent cygwin and others find.exe from being used
|
||||
set FIND_EXE="find.exe"
|
||||
if exist "%SystemRoot%\system32\find.exe" set FIND_EXE="%SystemRoot%\system32\find.exe"
|
||||
if exist "%SystemRoot%\command\find.exe" set FIND_EXE="%SystemRoot%\command\find.exe"
|
||||
|
||||
:check_JAVA_HOME
|
||||
@rem Make sure we have a valid JAVA_HOME
|
||||
if not "%JAVA_HOME%" == "" goto have_JAVA_HOME
|
||||
|
||||
echo.
|
||||
echo ERROR: Environment variable JAVA_HOME has not been set.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo.
|
||||
goto end
|
||||
|
||||
:have_JAVA_HOME
|
||||
@rem Remove trailing slash from JAVA_HOME if found
|
||||
if "%JAVA_HOME:~-1%"=="\" SET JAVA_HOME=%JAVA_HOME:~0,-1%
|
||||
|
||||
@rem Validate JAVA_HOME
|
||||
%COMMAND_COM% /C DIR "%JAVA_HOME%" 2>&1 | %FIND_EXE% /I /C "%JAVA_HOME%" >nul
|
||||
if not errorlevel 1 goto check_GRAILS_HOME
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
echo.
|
||||
goto end
|
||||
|
||||
:check_GRAILS_HOME
|
||||
@rem Define GRAILS_HOME if not set
|
||||
if "%GRAILS_HOME%" == "" set GRAILS_HOME=%DIRNAME%..
|
||||
|
||||
@rem Remove trailing slash from GRAILS_HOME if found
|
||||
if "%GRAILS_HOME:~-1%"=="\" SET GRAILS_HOME=%GRAILS_HOME:~0,-1%
|
||||
|
||||
:init
|
||||
|
||||
for %%x in ("%USERPROFILE%") do set SHORTHOME=%%~fsx
|
||||
if "x%GRAILS_AGENT_CACHE_DIR%" == "x" set GRAILS_AGENT_CACHE_DIR=%SHORTHOME%/.grails/2.5.2/
|
||||
set SPRINGLOADED_PARAMS="profile=grails;cacheDir=%GRAILS_AGENT_CACHE_DIR%"
|
||||
if not exist "%GRAILS_AGENT_CACHE_DIR%" mkdir "%GRAILS_AGENT_CACHE_DIR%"
|
||||
|
||||
if "%GRAILS_NO_PERMGEN%" == "" (
|
||||
type "%JAVA_HOME%\include\classfile_constants.h" 2>nul | findstr /R /C:"#define JVM_CLASSFILE_MAJOR_VERSION 5[23]" >nul
|
||||
if not errorlevel 1 set GRAILS_NO_PERMGEN=1
|
||||
)
|
||||
|
||||
set AGENT_STRING=-javaagent:wrapper/springloaded-1.2.4.RELEASE.jar -Xverify:none -Dspringloaded.synchronize=true -Djdk.reflect.allowGetCallerClass=true -Dspringloaded=\"%SPRINGLOADED_PARAMS%\"
|
||||
set DISABLE_RELOADING=
|
||||
if "%GRAILS_OPTS%" == "" (
|
||||
set GRAILS_OPTS=-server -Xmx768M -Xms64M -Dfile.encoding=UTF-8
|
||||
if not "%GRAILS_NO_PERMGEN%" == "1" (
|
||||
set GRAILS_OPTS=-server -Xmx768M -Xms64M -XX:PermSize=32m -XX:MaxPermSize=256m -Dfile.encoding=UTF-8
|
||||
)
|
||||
)
|
||||
|
||||
@rem Get command-line arguments, handling Windows variants
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set CP=
|
||||
set INTERACTIVE=true
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
set CURR_ARG=%~1
|
||||
if "%CURR_ARG:~0,2%" == "-D" (
|
||||
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %~1=%~2
|
||||
shift
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "x-cp" (
|
||||
set CP=%~2
|
||||
shift
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "x-debug" (
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Xdebug -Xnoagent -Dgrails.full.stacktrace=true -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "x-classpath" (
|
||||
set CP=%~2
|
||||
shift
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "x-reloading" (
|
||||
set AGENT=%AGENT_STRING%
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "xrun-app" (
|
||||
set AGENT=%AGENT_STRING%
|
||||
set INTERACTIVE=
|
||||
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
if "x%~1" == "x-noreloading" (
|
||||
set DISABLE_RELOADING=true
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
)
|
||||
set INTERACTIVE=
|
||||
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
|
||||
shift
|
||||
goto win9xME_args_slurp
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
set STARTER_CLASSPATH=wrapper/grails-wrapper-runtime-2.5.2.jar;wrapper;.
|
||||
|
||||
if exist "%USERPROFILE%/.groovy/init.bat" call "%USERPROFILE%/.groovy/init.bat"
|
||||
|
||||
@rem Setting a classpath using the -cp or -classpath option means not to use
|
||||
@rem the global classpath. Groovy behaves then the same as the java interpreter
|
||||
|
||||
if "x" == "x%CLASSPATH%" goto after_classpath
|
||||
set CP=%CP%;%CLASSPATH%
|
||||
:after_classpath
|
||||
|
||||
if "x%DISABLE_RELOADING%" == "xtrue" (
|
||||
set AGENT=
|
||||
) else (
|
||||
if "x%INTERACTIVE%" == "xtrue" (
|
||||
set AGENT=%AGENT_STRING%
|
||||
)
|
||||
)
|
||||
|
||||
set STARTER_MAIN_CLASS=org.grails.wrapper.GrailsWrapper
|
||||
set STARTER_CONF=%GRAILS_HOME%\conf\groovy-starter.conf
|
||||
|
||||
set JAVA_EXE=%JAVA_HOME%\bin\java.exe
|
||||
set TOOLS_JAR=%JAVA_HOME%\lib\tools.jar
|
||||
|
||||
set JAVA_OPTS=%GRAILS_OPTS% %JAVA_OPTS% %AGENT%
|
||||
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dprogram.name="%PROGNAME%"
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dgrails.home="%GRAILS_HOME%"
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dgrails.version=2.5.2
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dbase.dir=.
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dtools.jar="%TOOLS_JAR%"
|
||||
set JAVA_OPTS=%JAVA_OPTS% -Dgroovy.starter.conf="%STARTER_CONF%"
|
||||
|
||||
if exist "%USERPROFILE%/.groovy/postinit.bat" call "%USERPROFILE%/.groovy/postinit.bat"
|
||||
|
||||
@rem Execute Grails
|
||||
CALL "%JAVA_EXE%" %JAVA_OPTS% -classpath "%STARTER_CLASSPATH%" %STARTER_MAIN_CLASS% --main %CLASS% --conf "%STARTER_CONF%" --classpath "%CP%" "%CMD_LINE_ARGS%"
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
@rem Optional pause the batch file
|
||||
if "%GROOVY_BATCH_PAUSE%" == "on" pause
|
@ -1,28 +0,0 @@
|
||||
<ivy-module version="2.0">
|
||||
<info organisation="org.example" module="lti"/>
|
||||
<configurations defaultconfmapping="build->default;compile->compile(*),master(*);test,runtime->runtime(*),master(*)">
|
||||
<conf name="build"/>
|
||||
<conf name="compile"/>
|
||||
<conf name="test" extends="compile"/>
|
||||
<conf name="runtime" extends="compile"/>
|
||||
</configurations>
|
||||
<dependencies>
|
||||
<dependency org="org.grails" name="grails-bootstrap" rev="1.1.1" conf="build"/>
|
||||
<dependency org="org.grails" name="grails-scripts" rev="1.1.1" conf="build"/>
|
||||
<dependency org="org.grails" name="grails-gorm" rev="1.1.1" conf="compile"/>
|
||||
<dependency org="org.grails" name="grails-web" rev="1.1.1" conf="compile"/>
|
||||
<dependency org="org.grails" name="grails-test" rev="1.1.1" conf="test"/>
|
||||
<dependency org="org.slf4j" name="slf4j-log4j12" rev="1.5.5" conf="runtime"/>
|
||||
<dependency org="opensymphony" name="oscache" rev="2.4" conf="runtime">
|
||||
<exclude org="javax.jms" module="jms" name="*" type="*" ext="*" conf="" matcher="exact"/>
|
||||
<exclude org="commons-logging" module="commons-logging" name="*" type="*" ext="*" conf="" matcher="exact"/>
|
||||
<exclude org="javax.servlet" module="servlet-api" name="*" type="*" ext="*" conf="" matcher="exact"/>
|
||||
</dependency>
|
||||
<dependency org="hsqldb" name="hsqldb" rev="1.8.0.5" conf="runtime"/>
|
||||
<dependency org="net.sf.ehcache" name="ehcache" rev="1.5.0" conf="runtime"/>
|
||||
<!--
|
||||
<dependency org="mysql" name="mysql-connector-java" rev="5.1.6" conf="runtime"/>
|
||||
<dependency org="postgresql" name="postgresql" rev="8.3-603.jdbc3" conf="runtime"/>
|
||||
-->
|
||||
</dependencies>
|
||||
</ivy-module>
|
@ -1,15 +0,0 @@
|
||||
<ivysettings>
|
||||
<settings defaultResolver="codehaus-plus"/>
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-public.xml" />
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-local.xml" />
|
||||
<include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
|
||||
<resolvers>
|
||||
<chain name="codehaus-plus" dual="true">
|
||||
<ibiblio name="codehaus-snapshots" root="http://snapshots.repository.codehaus.org" m2compatible="true" changingPattern=".*SNAPSHOT"/>
|
||||
<ibiblio name="codehaus" root="http://repository.codehaus.org" m2compatible="true"/>
|
||||
<ibiblio name="javanet" root="http://download.java.net/maven/2/" m2compatible="true"/>
|
||||
<resolver ref="public"/>
|
||||
</chain>
|
||||
</resolvers>
|
||||
</ivysettings>
|
@ -79,10 +79,16 @@ public class Proxy {
|
||||
url += "&attendeePW=" + attendeePW;
|
||||
url += "&welcome=" + getStringEncoded(welcome);
|
||||
url += "&logoutURL=" + getStringEncoded(logoutURL);
|
||||
url += "&maxParticipants=" + maxParticipants;
|
||||
url += "&voiceBridge=" + voiceBridge;
|
||||
url += "&dialNumber=" + dialNumber;
|
||||
url += "&webVoice=" + webVoice;
|
||||
if ( maxParticipants != null ) {
|
||||
url += "&maxParticipants=" + maxParticipants;
|
||||
}
|
||||
if ( dialNumber != null ) {
|
||||
url += "&dialNumber=" + dialNumber;
|
||||
}
|
||||
if ( webVoice != null ) {
|
||||
url += "&webVoice=" + webVoice;
|
||||
}
|
||||
url += "&record=" + record;
|
||||
url += "&duration=" + duration;
|
||||
url += "&" + meta;
|
||||
|
@ -6,7 +6,6 @@
|
||||
<bean id="grailsApplication" class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
|
||||
<description>Grails application factory bean</description>
|
||||
<property name="grailsDescriptor" value="/WEB-INF/grails.xml" />
|
||||
<property name="grailsResourceLoader" ref="grailsResourceLoader" />
|
||||
</bean>
|
||||
|
||||
<bean id="pluginManager" class="org.codehaus.groovy.grails.plugins.GrailsPluginManagerFactoryBean">
|
||||
@ -22,8 +21,6 @@
|
||||
<property name="pluginManager" ref="pluginManager" />
|
||||
</bean>
|
||||
|
||||
<bean id="grailsResourceLoader" class="org.codehaus.groovy.grails.commons.GrailsResourceLoaderFactoryBean" />
|
||||
|
||||
<bean id="characterEncodingFilter" class="org.springframework.web.filter.CharacterEncodingFilter">
|
||||
<property name="encoding">
|
||||
<value>utf-8</value>
|
||||
|
@ -4,12 +4,11 @@
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
|
||||
http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
|
||||
version="2.0">
|
||||
<description>The Grails (Groovy on Rails) custom tag library</description>
|
||||
<description>The Grails custom tag library</description>
|
||||
<tlib-version>0.2</tlib-version>
|
||||
<short-name>grails</short-name>
|
||||
<uri>http://grails.codehaus.org/tags</uri>
|
||||
|
||||
|
||||
<tag>
|
||||
<name>link</name>
|
||||
<tag-class>org.codehaus.groovy.grails.web.taglib.jsp.JspLinkTag</tag-class>
|
||||
@ -541,7 +540,7 @@
|
||||
<scope>NESTED</scope>
|
||||
</variable>
|
||||
<attribute>
|
||||
<name>name</name>
|
||||
<name>tagName</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
2411
bbb-lti/web-app/WEB-INF/tld/spring-form.tld
Normal file
@ -1,311 +1,457 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1" ?>
|
||||
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN" "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
|
||||
|
||||
<taglib>
|
||||
|
||||
<tlib-version>1.1.1</tlib-version>
|
||||
|
||||
<jsp-version>1.2</jsp-version>
|
||||
|
||||
<short-name>Spring</short-name>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<taglib xmlns="http://java.sun.com/xml/ns/j2ee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd"
|
||||
version="2.0">
|
||||
|
||||
<description>Spring Framework JSP Tag Library</description>
|
||||
<tlib-version>3.0</tlib-version>
|
||||
<short-name>spring</short-name>
|
||||
<uri>http://www.springframework.org/tags</uri>
|
||||
|
||||
<description>Spring Framework JSP Tag Library. Authors: Rod Johnson, Juergen Hoeller</description>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>htmlEscape</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Sets default HTML escape value for the current page.
|
||||
Overrides a "defaultHtmlEscape" context-param in web.xml, if any.
|
||||
</description>
|
||||
|
||||
<name>htmlEscape</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.HtmlEscapeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>Set the default value for HTML escaping, to be put
|
||||
into the current PageContext.</description>
|
||||
<name>defaultHtmlEscape</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>escapeBody</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Escapes its enclosed body content, applying HTML escaping and/or JavaScript escaping.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>escapeBody</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.EscapeBodyTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value. Overrides the
|
||||
default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set JavaScript escaping for this tag, as boolean value.
|
||||
Default is false.</description>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>message</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Retrieves the message with the given code, or text if code isn't resolvable.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>message</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.MessageTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>A MessageSourceResolvable argument (direct or through JSP EL).
|
||||
Fits nicely when used in conjunction with Spring's own validation error
|
||||
classes which all implement the MessageSourceResolvable interface. For
|
||||
example, this allows you to iterate over all of the errors in a form,
|
||||
passing each error (using a runtime expression) as the value of this
|
||||
'message' attribute, thus effecting the easy display of such error
|
||||
messages.</description>
|
||||
<name>message</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The code (key) to use when looking up the message.
|
||||
If code is not provided, the text attribute will be used.</description>
|
||||
<name>code</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set optional message arguments for this tag, as a
|
||||
(comma-)delimited String (each String argument can contain JSP EL),
|
||||
an Object array (used as argument array), or a single Object (used
|
||||
as single argument).</description>
|
||||
<name>arguments</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The separator character to be used for splitting the
|
||||
arguments string value; defaults to a 'comma' (',').</description>
|
||||
<name>argumentSeparator</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Default text to output when a message for the given code
|
||||
could not be found. If both text and code are not set, the tag will
|
||||
output null.</description>
|
||||
<name>text</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The string to use when binding the result to the page,
|
||||
request, session or application scope. If not specified, the result
|
||||
gets outputted to the writer (i.e. typically directly to the JSP).</description>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The scope to use when exporting the result to a variable.
|
||||
This attribute is only used when var is also set. Possible values are
|
||||
page, request, session and application.</description>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value.
|
||||
Overrides the default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set JavaScript escaping for this tag, as boolean value. Default is false.</description>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>theme</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Retrieves the theme message with the given code, or text if code isn't resolvable.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>theme</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.ThemeTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>A MessageSourceResolvable argument (direct or through JSP EL).</description>
|
||||
<name>message</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The code (key) to use when looking up the message.
|
||||
If code is not provided, the text attribute will be used.</description>
|
||||
<name>code</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set optional message arguments for this tag, as a
|
||||
(comma-)delimited String (each String argument can contain JSP EL),
|
||||
an Object array (used as argument array), or a single Object (used
|
||||
as single argument).</description>
|
||||
<name>arguments</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The separator character to be used for splitting the
|
||||
arguments string value; defaults to a 'comma' (',').</description>
|
||||
<name>argumentSeparator</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Default text to output when a message for the given code
|
||||
could not be found. If both text and code are not set, the tag will
|
||||
output null.</description>
|
||||
<name>text</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The string to use when binding the result to the page,
|
||||
request, session or application scope. If not specified, the result
|
||||
gets outputted to the writer (i.e. typically directly to the JSP).</description>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The scope to use when exporting the result to a variable.
|
||||
This attribute is only used when var is also set. Possible values are
|
||||
page, request, session and application.</description>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value.
|
||||
Overrides the default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set JavaScript escaping for this tag, as boolean value. Default is false.</description>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>hasBindErrors</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides Errors instance in case of bind errors.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>hasBindErrors</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindErrorsTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<variable>
|
||||
<name-given>errors</name-given>
|
||||
<variable-class>org.springframework.validation.Errors</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<description>The name of the bean in the request, that needs to be
|
||||
inspected for errors. If errors are available for this bean, they
|
||||
will be bound under the 'errors' key.</description>
|
||||
<name>name</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value.
|
||||
Overrides the default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>nestedPath</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Sets a nested path to be used by the bind tag's path.
|
||||
</description>
|
||||
|
||||
<name>nestedPath</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.NestedPathTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<variable>
|
||||
<name-given>nestedPath</name-given>
|
||||
<variable-class>java.lang.String</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<description>Set the path that this tag should apply. E.g. 'customer'
|
||||
to allow bind paths like 'address.street' rather than
|
||||
'customer.address.street'.</description>
|
||||
<name>path</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>bind</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides BindStatus object for the given bind path.
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>bind</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.BindTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<variable>
|
||||
<name-given>status</name-given>
|
||||
<variable-class>org.springframework.web.servlet.support.BindStatus</variable-class>
|
||||
</variable>
|
||||
|
||||
<attribute>
|
||||
<description>The path to the bean or bean property to bind status
|
||||
information for. For instance account.name, company.address.zipCode
|
||||
or just employee. The status object will exported to the page scope,
|
||||
specifically for this bean or bean property</description>
|
||||
<name>path</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set whether to ignore a nested path, if any. Default is to not ignore.</description>
|
||||
<name>ignoreNestedPath</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value. Overrides
|
||||
the default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
</tag>
|
||||
|
||||
|
||||
<tag>
|
||||
|
||||
<name>transform</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
|
||||
<description>
|
||||
Provides transformation of variables to Strings, using an appropriate
|
||||
custom PropertyEditor from BindTag (can only be used inside BindTag).
|
||||
The HTML escaping flag participates in a page-wide or application-wide setting
|
||||
(i.e. by HtmlEscapeTag or a "defaultHtmlEscape" context-param in web.xml).
|
||||
(i.e. by HtmlEscapeTag or a 'defaultHtmlEscape' context-param in web.xml).
|
||||
</description>
|
||||
|
||||
<name>transform</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.TransformTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>The value to transform. This is the actual object you want
|
||||
to have transformed (for instance a Date). Using the PropertyEditor that
|
||||
is currently in use by the 'spring:bind' tag.</description>
|
||||
<name>value</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The string to use when binding the result to the page,
|
||||
request, session or application scope. If not specified, the result gets
|
||||
outputted to the writer (i.e. typically directly to the JSP).</description>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>The scope to use when exported the result to a variable.
|
||||
This attribute is only used when var is also set. Possible values are
|
||||
page, request, session and application.</description>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as boolean value. Overrides
|
||||
the default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<description>URL tag based on the JSTL c:url tag. This variant is fully
|
||||
backwards compatible with the standard tag. Enhancements include support
|
||||
for URL template parameters.</description>
|
||||
<name>url</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.UrlTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>The URL to build. This value can include template place holders
|
||||
that are replaced with the URL encoded value of the named parameter. Parameters
|
||||
must be defined using the param tag inside the body of this tag.</description>
|
||||
<name>value</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Specifies a remote application context path. The default is the
|
||||
current application context path.</description>
|
||||
<name>context</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The name of the variable to export the URL value to.</description>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The scope for the var. 'application', 'session', 'request' and
|
||||
'page' scopes are supported. Defaults to page scope. This attribute has no
|
||||
effect unless the var attribute is also defined.</description>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as a boolean value. Overrides the
|
||||
default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Set JavaScript escaping for this tag, as a boolean value.
|
||||
Default is false.</description>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<description>Parameter tag based on the JSTL c:param tag. The sole purpose is to
|
||||
support params inside the spring:url tag.</description>
|
||||
<name>param</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.ParamTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>The name of the parameter.</description>
|
||||
<name>name</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The value of the parameter.</description>
|
||||
<name>value</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
<tag>
|
||||
<description>Evaluates a Spring expression (SpEL) and either prints the result or assigns it to a variable.</description>
|
||||
<name>eval</name>
|
||||
<tag-class>org.springframework.web.servlet.tags.EvalTag</tag-class>
|
||||
<body-content>JSP</body-content>
|
||||
<attribute>
|
||||
<description>The expression to evaluate.</description>
|
||||
<name>expression</name>
|
||||
<required>true</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The name of the variable to export the evaluation result to.</description>
|
||||
<name>var</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>The scope for the var. 'application', 'session', 'request' and
|
||||
'page' scopes are supported. Defaults to page scope. This attribute has no
|
||||
effect unless the var attribute is also defined.</description>
|
||||
<name>scope</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Set HTML escaping for this tag, as a boolean value. Overrides the
|
||||
default HTML escaping setting for the current page.</description>
|
||||
<name>htmlEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
<attribute>
|
||||
<description>Set JavaScript escaping for this tag, as a boolean value. Default is false.</description>
|
||||
<name>javaScriptEscape</name>
|
||||
<required>false</required>
|
||||
<rtexprvalue>true</rtexprvalue>
|
||||
</attribute>
|
||||
</tag>
|
||||
|
||||
</taglib>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 34 KiB |
1951
bbb-lti/web-app/js/bootstrap.js
vendored
7
bbb-lti/web-app/js/prototype/animation.js
vendored
136
bbb-lti/web-app/js/prototype/builder.js
vendored
@ -1,136 +0,0 @@
|
||||
// script.aculo.us builder.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
var Builder = {
|
||||
NODEMAP: {
|
||||
AREA: 'map',
|
||||
CAPTION: 'table',
|
||||
COL: 'table',
|
||||
COLGROUP: 'table',
|
||||
LEGEND: 'fieldset',
|
||||
OPTGROUP: 'select',
|
||||
OPTION: 'select',
|
||||
PARAM: 'object',
|
||||
TBODY: 'table',
|
||||
TD: 'table',
|
||||
TFOOT: 'table',
|
||||
TH: 'table',
|
||||
THEAD: 'table',
|
||||
TR: 'table'
|
||||
},
|
||||
// note: For Firefox < 1.5, OPTION and OPTGROUP tags are currently broken,
|
||||
// due to a Firefox bug
|
||||
node: function(elementName) {
|
||||
elementName = elementName.toUpperCase();
|
||||
|
||||
// try innerHTML approach
|
||||
var parentTag = this.NODEMAP[elementName] || 'div';
|
||||
var parentElement = document.createElement(parentTag);
|
||||
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
||||
parentElement.innerHTML = "<" + elementName + "></" + elementName + ">";
|
||||
} catch(e) {}
|
||||
var element = parentElement.firstChild || null;
|
||||
|
||||
// see if browser added wrapping tags
|
||||
if(element && (element.tagName.toUpperCase() != elementName))
|
||||
element = element.getElementsByTagName(elementName)[0];
|
||||
|
||||
// fallback to createElement approach
|
||||
if(!element) element = document.createElement(elementName);
|
||||
|
||||
// abort if nothing could be created
|
||||
if(!element) return;
|
||||
|
||||
// attributes (or text)
|
||||
if(arguments[1])
|
||||
if(this._isStringOrNumber(arguments[1]) ||
|
||||
(arguments[1] instanceof Array) ||
|
||||
arguments[1].tagName) {
|
||||
this._children(element, arguments[1]);
|
||||
} else {
|
||||
var attrs = this._attributes(arguments[1]);
|
||||
if(attrs.length) {
|
||||
try { // prevent IE "feature": http://dev.rubyonrails.org/ticket/2707
|
||||
parentElement.innerHTML = "<" +elementName + " " +
|
||||
attrs + "></" + elementName + ">";
|
||||
} catch(e) {}
|
||||
element = parentElement.firstChild || null;
|
||||
// workaround firefox 1.0.X bug
|
||||
if(!element) {
|
||||
element = document.createElement(elementName);
|
||||
for(attr in arguments[1])
|
||||
element[attr == 'class' ? 'className' : attr] = arguments[1][attr];
|
||||
}
|
||||
if(element.tagName.toUpperCase() != elementName)
|
||||
element = parentElement.getElementsByTagName(elementName)[0];
|
||||
}
|
||||
}
|
||||
|
||||
// text, or array of children
|
||||
if(arguments[2])
|
||||
this._children(element, arguments[2]);
|
||||
|
||||
return element;
|
||||
},
|
||||
_text: function(text) {
|
||||
return document.createTextNode(text);
|
||||
},
|
||||
|
||||
ATTR_MAP: {
|
||||
'className': 'class',
|
||||
'htmlFor': 'for'
|
||||
},
|
||||
|
||||
_attributes: function(attributes) {
|
||||
var attrs = [];
|
||||
for(attribute in attributes)
|
||||
attrs.push((attribute in this.ATTR_MAP ? this.ATTR_MAP[attribute] : attribute) +
|
||||
'="' + attributes[attribute].toString().escapeHTML().gsub(/"/,'"') + '"');
|
||||
return attrs.join(" ");
|
||||
},
|
||||
_children: function(element, children) {
|
||||
if(children.tagName) {
|
||||
element.appendChild(children);
|
||||
return;
|
||||
}
|
||||
if(typeof children=='object') { // array can hold nodes and text
|
||||
children.flatten().each( function(e) {
|
||||
if(typeof e=='object')
|
||||
element.appendChild(e)
|
||||
else
|
||||
if(Builder._isStringOrNumber(e))
|
||||
element.appendChild(Builder._text(e));
|
||||
});
|
||||
} else
|
||||
if(Builder._isStringOrNumber(children))
|
||||
element.appendChild(Builder._text(children));
|
||||
},
|
||||
_isStringOrNumber: function(param) {
|
||||
return(typeof param=='string' || typeof param=='number');
|
||||
},
|
||||
build: function(html) {
|
||||
var element = this.node('div');
|
||||
$(element).update(html.strip());
|
||||
return element.down();
|
||||
},
|
||||
dump: function(scope) {
|
||||
if(typeof scope != 'object' && typeof scope != 'function') scope = window; //global scope
|
||||
|
||||
var tags = ("A ABBR ACRONYM ADDRESS APPLET AREA B BASE BASEFONT BDO BIG BLOCKQUOTE BODY " +
|
||||
"BR BUTTON CAPTION CENTER CITE CODE COL COLGROUP DD DEL DFN DIR DIV DL DT EM FIELDSET " +
|
||||
"FONT FORM FRAME FRAMESET H1 H2 H3 H4 H5 H6 HEAD HR HTML I IFRAME IMG INPUT INS ISINDEX "+
|
||||
"KBD LABEL LEGEND LI LINK MAP MENU META NOFRAMES NOSCRIPT OBJECT OL OPTGROUP OPTION P "+
|
||||
"PARAM PRE Q S SAMP SCRIPT SELECT SMALL SPAN STRIKE STRONG STYLE SUB SUP TABLE TBODY TD "+
|
||||
"TEXTAREA TFOOT TH THEAD TITLE TR TT U UL VAR").split(/\s+/);
|
||||
|
||||
tags.each( function(tag){
|
||||
scope[tag] = function() {
|
||||
return Builder.node.apply(Builder, [tag].concat($A(arguments)));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
965
bbb-lti/web-app/js/prototype/controls.js
vendored
@ -1,965 +0,0 @@
|
||||
// script.aculo.us controls.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
|
||||
// (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
|
||||
// Contributors:
|
||||
// Richard Livsey
|
||||
// Rahul Bhargava
|
||||
// Rob Wills
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
// Autocompleter.Base handles all the autocompletion functionality
|
||||
// that's independent of the data source for autocompletion. This
|
||||
// includes drawing the autocompletion menu, observing keyboard
|
||||
// and mouse events, and similar.
|
||||
//
|
||||
// Specific autocompleters need to provide, at the very least,
|
||||
// a getUpdatedChoices function that will be invoked every time
|
||||
// the text inside the monitored textbox changes. This method
|
||||
// should get the text for which to provide autocompletion by
|
||||
// invoking this.getToken(), NOT by directly accessing
|
||||
// this.element.value. This is to allow incremental tokenized
|
||||
// autocompletion. Specific auto-completion logic (AJAX, etc)
|
||||
// belongs in getUpdatedChoices.
|
||||
//
|
||||
// Tokenized incremental autocompletion is enabled automatically
|
||||
// when an autocompleter is instantiated with the 'tokens' option
|
||||
// in the options parameter, e.g.:
|
||||
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
|
||||
// will incrementally autocomplete with a comma as the token.
|
||||
// Additionally, ',' in the above example can be replaced with
|
||||
// a token array, e.g. { tokens: [',', '\n'] } which
|
||||
// enables autocompletion on multiple tokens. This is most
|
||||
// useful when one of the tokens is \n (a newline), as it
|
||||
// allows smart autocompletion after linebreaks.
|
||||
|
||||
if(typeof Effect == 'undefined')
|
||||
throw("controls.js requires including script.aculo.us' effects.js library");
|
||||
|
||||
var Autocompleter = { }
|
||||
Autocompleter.Base = Class.create({
|
||||
baseInitialize: function(element, update, options) {
|
||||
element = $(element)
|
||||
this.element = element;
|
||||
this.update = $(update);
|
||||
this.hasFocus = false;
|
||||
this.changed = false;
|
||||
this.active = false;
|
||||
this.index = 0;
|
||||
this.entryCount = 0;
|
||||
this.oldElementValue = this.element.value;
|
||||
|
||||
if(this.setOptions)
|
||||
this.setOptions(options);
|
||||
else
|
||||
this.options = options || { };
|
||||
|
||||
this.options.paramName = this.options.paramName || this.element.name;
|
||||
this.options.tokens = this.options.tokens || [];
|
||||
this.options.frequency = this.options.frequency || 0.4;
|
||||
this.options.minChars = this.options.minChars || 1;
|
||||
this.options.onShow = this.options.onShow ||
|
||||
function(element, update){
|
||||
if(!update.style.position || update.style.position=='absolute') {
|
||||
update.style.position = 'absolute';
|
||||
Position.clone(element, update, {
|
||||
setHeight: false,
|
||||
offsetTop: element.offsetHeight
|
||||
});
|
||||
}
|
||||
Effect.Appear(update,{duration:0.15});
|
||||
};
|
||||
this.options.onHide = this.options.onHide ||
|
||||
function(element, update){ new Effect.Fade(update,{duration:0.15}) };
|
||||
|
||||
if(typeof(this.options.tokens) == 'string')
|
||||
this.options.tokens = new Array(this.options.tokens);
|
||||
// Force carriage returns as token delimiters anyway
|
||||
if (!this.options.tokens.include('\n'))
|
||||
this.options.tokens.push('\n');
|
||||
|
||||
this.observer = null;
|
||||
|
||||
this.element.setAttribute('autocomplete','off');
|
||||
|
||||
Element.hide(this.update);
|
||||
|
||||
Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
|
||||
Event.observe(this.element, 'keypress', this.onKeyPress.bindAsEventListener(this));
|
||||
},
|
||||
|
||||
show: function() {
|
||||
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
||||
if(!this.iefix &&
|
||||
(Prototype.Browser.IE) &&
|
||||
(Element.getStyle(this.update, 'position')=='absolute')) {
|
||||
new Insertion.After(this.update,
|
||||
'<iframe id="' + this.update.id + '_iefix" '+
|
||||
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
||||
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
|
||||
this.iefix = $(this.update.id+'_iefix');
|
||||
}
|
||||
if(this.iefix) setTimeout(this.fixIEOverlapping.bind(this), 50);
|
||||
},
|
||||
|
||||
fixIEOverlapping: function() {
|
||||
Position.clone(this.update, this.iefix, {setTop:(!this.update.style.height)});
|
||||
this.iefix.style.zIndex = 1;
|
||||
this.update.style.zIndex = 2;
|
||||
Element.show(this.iefix);
|
||||
},
|
||||
|
||||
hide: function() {
|
||||
this.stopIndicator();
|
||||
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
|
||||
if(this.iefix) Element.hide(this.iefix);
|
||||
},
|
||||
|
||||
startIndicator: function() {
|
||||
if(this.options.indicator) Element.show(this.options.indicator);
|
||||
},
|
||||
|
||||
stopIndicator: function() {
|
||||
if(this.options.indicator) Element.hide(this.options.indicator);
|
||||
},
|
||||
|
||||
onKeyPress: function(event) {
|
||||
if(this.active)
|
||||
switch(event.keyCode) {
|
||||
case Event.KEY_TAB:
|
||||
case Event.KEY_RETURN:
|
||||
this.selectEntry();
|
||||
Event.stop(event);
|
||||
case Event.KEY_ESC:
|
||||
this.hide();
|
||||
this.active = false;
|
||||
Event.stop(event);
|
||||
return;
|
||||
case Event.KEY_LEFT:
|
||||
case Event.KEY_RIGHT:
|
||||
return;
|
||||
case Event.KEY_UP:
|
||||
this.markPrevious();
|
||||
this.render();
|
||||
if(Prototype.Browser.WebKit) Event.stop(event);
|
||||
return;
|
||||
case Event.KEY_DOWN:
|
||||
this.markNext();
|
||||
this.render();
|
||||
if(Prototype.Browser.WebKit) Event.stop(event);
|
||||
return;
|
||||
}
|
||||
else
|
||||
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
||||
(Prototype.Browser.WebKit > 0 && event.keyCode == 0)) return;
|
||||
|
||||
this.changed = true;
|
||||
this.hasFocus = true;
|
||||
|
||||
if(this.observer) clearTimeout(this.observer);
|
||||
this.observer =
|
||||
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
||||
},
|
||||
|
||||
activate: function() {
|
||||
this.changed = false;
|
||||
this.hasFocus = true;
|
||||
this.getUpdatedChoices();
|
||||
},
|
||||
|
||||
onHover: function(event) {
|
||||
var element = Event.findElement(event, 'LI');
|
||||
if(this.index != element.autocompleteIndex)
|
||||
{
|
||||
this.index = element.autocompleteIndex;
|
||||
this.render();
|
||||
}
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
onClick: function(event) {
|
||||
var element = Event.findElement(event, 'LI');
|
||||
this.index = element.autocompleteIndex;
|
||||
this.selectEntry();
|
||||
this.hide();
|
||||
},
|
||||
|
||||
onBlur: function(event) {
|
||||
// needed to make click events working
|
||||
setTimeout(this.hide.bind(this), 250);
|
||||
this.hasFocus = false;
|
||||
this.active = false;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if(this.entryCount > 0) {
|
||||
for (var i = 0; i < this.entryCount; i++)
|
||||
this.index==i ?
|
||||
Element.addClassName(this.getEntry(i),"selected") :
|
||||
Element.removeClassName(this.getEntry(i),"selected");
|
||||
if(this.hasFocus) {
|
||||
this.show();
|
||||
this.active = true;
|
||||
}
|
||||
} else {
|
||||
this.active = false;
|
||||
this.hide();
|
||||
}
|
||||
},
|
||||
|
||||
markPrevious: function() {
|
||||
if(this.index > 0) this.index--
|
||||
else this.index = this.entryCount-1;
|
||||
this.getEntry(this.index).scrollIntoView(true);
|
||||
},
|
||||
|
||||
markNext: function() {
|
||||
if(this.index < this.entryCount-1) this.index++
|
||||
else this.index = 0;
|
||||
this.getEntry(this.index).scrollIntoView(false);
|
||||
},
|
||||
|
||||
getEntry: function(index) {
|
||||
return this.update.firstChild.childNodes[index];
|
||||
},
|
||||
|
||||
getCurrentEntry: function() {
|
||||
return this.getEntry(this.index);
|
||||
},
|
||||
|
||||
selectEntry: function() {
|
||||
this.active = false;
|
||||
this.updateElement(this.getCurrentEntry());
|
||||
},
|
||||
|
||||
updateElement: function(selectedElement) {
|
||||
if (this.options.updateElement) {
|
||||
this.options.updateElement(selectedElement);
|
||||
return;
|
||||
}
|
||||
var value = '';
|
||||
if (this.options.select) {
|
||||
var nodes = $(selectedElement).select('.' + this.options.select) || [];
|
||||
if(nodes.length>0) value = Element.collectTextNodes(nodes[0], this.options.select);
|
||||
} else
|
||||
value = Element.collectTextNodesIgnoreClass(selectedElement, 'informal');
|
||||
|
||||
var bounds = this.getTokenBounds();
|
||||
if (bounds[0] != -1) {
|
||||
var newValue = this.element.value.substr(0, bounds[0]);
|
||||
var whitespace = this.element.value.substr(bounds[0]).match(/^\s+/);
|
||||
if (whitespace)
|
||||
newValue += whitespace[0];
|
||||
this.element.value = newValue + value + this.element.value.substr(bounds[1]);
|
||||
} else {
|
||||
this.element.value = value;
|
||||
}
|
||||
this.oldElementValue = this.element.value;
|
||||
this.element.focus();
|
||||
|
||||
if (this.options.afterUpdateElement)
|
||||
this.options.afterUpdateElement(this.element, selectedElement);
|
||||
},
|
||||
|
||||
updateChoices: function(choices) {
|
||||
if(!this.changed && this.hasFocus) {
|
||||
this.update.innerHTML = choices;
|
||||
Element.cleanWhitespace(this.update);
|
||||
Element.cleanWhitespace(this.update.down());
|
||||
|
||||
if(this.update.firstChild && this.update.down().childNodes) {
|
||||
this.entryCount =
|
||||
this.update.down().childNodes.length;
|
||||
for (var i = 0; i < this.entryCount; i++) {
|
||||
var entry = this.getEntry(i);
|
||||
entry.autocompleteIndex = i;
|
||||
this.addObservers(entry);
|
||||
}
|
||||
} else {
|
||||
this.entryCount = 0;
|
||||
}
|
||||
|
||||
this.stopIndicator();
|
||||
this.index = 0;
|
||||
|
||||
if(this.entryCount==1 && this.options.autoSelect) {
|
||||
this.selectEntry();
|
||||
this.hide();
|
||||
} else {
|
||||
this.render();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addObservers: function(element) {
|
||||
Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
|
||||
Event.observe(element, "click", this.onClick.bindAsEventListener(this));
|
||||
},
|
||||
|
||||
onObserverEvent: function() {
|
||||
this.changed = false;
|
||||
this.tokenBounds = null;
|
||||
if(this.getToken().length>=this.options.minChars) {
|
||||
this.getUpdatedChoices();
|
||||
} else {
|
||||
this.active = false;
|
||||
this.hide();
|
||||
}
|
||||
this.oldElementValue = this.element.value;
|
||||
},
|
||||
|
||||
getToken: function() {
|
||||
var bounds = this.getTokenBounds();
|
||||
return this.element.value.substring(bounds[0], bounds[1]).strip();
|
||||
},
|
||||
|
||||
getTokenBounds: function() {
|
||||
if (null != this.tokenBounds) return this.tokenBounds;
|
||||
var value = this.element.value;
|
||||
if (value.strip().empty()) return [-1, 0];
|
||||
var diff = arguments.callee.getFirstDifferencePos(value, this.oldElementValue);
|
||||
var offset = (diff == this.oldElementValue.length ? 1 : 0);
|
||||
var prevTokenPos = -1, nextTokenPos = value.length;
|
||||
var tp;
|
||||
for (var index = 0, l = this.options.tokens.length; index < l; ++index) {
|
||||
tp = value.lastIndexOf(this.options.tokens[index], diff + offset - 1);
|
||||
if (tp > prevTokenPos) prevTokenPos = tp;
|
||||
tp = value.indexOf(this.options.tokens[index], diff + offset);
|
||||
if (-1 != tp && tp < nextTokenPos) nextTokenPos = tp;
|
||||
}
|
||||
return (this.tokenBounds = [prevTokenPos + 1, nextTokenPos]);
|
||||
}
|
||||
});
|
||||
|
||||
Autocompleter.Base.prototype.getTokenBounds.getFirstDifferencePos = function(newS, oldS) {
|
||||
var boundary = Math.min(newS.length, oldS.length);
|
||||
for (var index = 0; index < boundary; ++index)
|
||||
if (newS[index] != oldS[index])
|
||||
return index;
|
||||
return boundary;
|
||||
};
|
||||
|
||||
Ajax.Autocompleter = Class.create(Autocompleter.Base, {
|
||||
initialize: function(element, update, url, options) {
|
||||
this.baseInitialize(element, update, options);
|
||||
this.options.asynchronous = true;
|
||||
this.options.onComplete = this.onComplete.bind(this);
|
||||
this.options.defaultParams = this.options.parameters || null;
|
||||
this.url = url;
|
||||
},
|
||||
|
||||
getUpdatedChoices: function() {
|
||||
this.startIndicator();
|
||||
|
||||
var entry = encodeURIComponent(this.options.paramName) + '=' +
|
||||
encodeURIComponent(this.getToken());
|
||||
|
||||
this.options.parameters = this.options.callback ?
|
||||
this.options.callback(this.element, entry) : entry;
|
||||
|
||||
if(this.options.defaultParams)
|
||||
this.options.parameters += '&' + this.options.defaultParams;
|
||||
|
||||
new Ajax.Request(this.url, this.options);
|
||||
},
|
||||
|
||||
onComplete: function(request) {
|
||||
this.updateChoices(request.responseText);
|
||||
}
|
||||
});
|
||||
|
||||
// The local array autocompleter. Used when you'd prefer to
|
||||
// inject an array of autocompletion options into the page, rather
|
||||
// than sending out Ajax queries, which can be quite slow sometimes.
|
||||
//
|
||||
// The constructor takes four parameters. The first two are, as usual,
|
||||
// the id of the monitored textbox, and id of the autocompletion menu.
|
||||
// The third is the array you want to autocomplete from, and the fourth
|
||||
// is the options block.
|
||||
//
|
||||
// Extra local autocompletion options:
|
||||
// - choices - How many autocompletion choices to offer
|
||||
//
|
||||
// - partialSearch - If false, the autocompleter will match entered
|
||||
// text only at the beginning of strings in the
|
||||
// autocomplete array. Defaults to true, which will
|
||||
// match text at the beginning of any *word* in the
|
||||
// strings in the autocomplete array. If you want to
|
||||
// search anywhere in the string, additionally set
|
||||
// the option fullSearch to true (default: off).
|
||||
//
|
||||
// - fullSsearch - Search anywhere in autocomplete array strings.
|
||||
//
|
||||
// - partialChars - How many characters to enter before triggering
|
||||
// a partial match (unlike minChars, which defines
|
||||
// how many characters are required to do any match
|
||||
// at all). Defaults to 2.
|
||||
//
|
||||
// - ignoreCase - Whether to ignore case when autocompleting.
|
||||
// Defaults to true.
|
||||
//
|
||||
// It's possible to pass in a custom function as the 'selector'
|
||||
// option, if you prefer to write your own autocompletion logic.
|
||||
// In that case, the other options above will not apply unless
|
||||
// you support them.
|
||||
|
||||
Autocompleter.Local = Class.create(Autocompleter.Base, {
|
||||
initialize: function(element, update, array, options) {
|
||||
this.baseInitialize(element, update, options);
|
||||
this.options.array = array;
|
||||
},
|
||||
|
||||
getUpdatedChoices: function() {
|
||||
this.updateChoices(this.options.selector(this));
|
||||
},
|
||||
|
||||
setOptions: function(options) {
|
||||
this.options = Object.extend({
|
||||
choices: 10,
|
||||
partialSearch: true,
|
||||
partialChars: 2,
|
||||
ignoreCase: true,
|
||||
fullSearch: false,
|
||||
selector: function(instance) {
|
||||
var ret = []; // Beginning matches
|
||||
var partial = []; // Inside matches
|
||||
var entry = instance.getToken();
|
||||
var count = 0;
|
||||
|
||||
for (var i = 0; i < instance.options.array.length &&
|
||||
ret.length < instance.options.choices ; i++) {
|
||||
|
||||
var elem = instance.options.array[i];
|
||||
var foundPos = instance.options.ignoreCase ?
|
||||
elem.toLowerCase().indexOf(entry.toLowerCase()) :
|
||||
elem.indexOf(entry);
|
||||
|
||||
while (foundPos != -1) {
|
||||
if (foundPos == 0 && elem.length != entry.length) {
|
||||
ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
|
||||
elem.substr(entry.length) + "</li>");
|
||||
break;
|
||||
} else if (entry.length >= instance.options.partialChars &&
|
||||
instance.options.partialSearch && foundPos != -1) {
|
||||
if (instance.options.fullSearch || /\s/.test(elem.substr(foundPos-1,1))) {
|
||||
partial.push("<li>" + elem.substr(0, foundPos) + "<strong>" +
|
||||
elem.substr(foundPos, entry.length) + "</strong>" + elem.substr(
|
||||
foundPos + entry.length) + "</li>");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foundPos = instance.options.ignoreCase ?
|
||||
elem.toLowerCase().indexOf(entry.toLowerCase(), foundPos + 1) :
|
||||
elem.indexOf(entry, foundPos + 1);
|
||||
|
||||
}
|
||||
}
|
||||
if (partial.length)
|
||||
ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
|
||||
return "<ul>" + ret.join('') + "</ul>";
|
||||
}
|
||||
}, options || { });
|
||||
}
|
||||
});
|
||||
|
||||
// AJAX in-place editor and collection editor
|
||||
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
|
||||
|
||||
// Use this if you notice weird scrolling problems on some browsers,
|
||||
// the DOM might be a bit confused when this gets called so do this
|
||||
// waits 1 ms (with setTimeout) until it does the activation
|
||||
Field.scrollFreeActivate = function(field) {
|
||||
setTimeout(function() {
|
||||
Field.activate(field);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
Ajax.InPlaceEditor = Class.create({
|
||||
initialize: function(element, url, options) {
|
||||
this.url = url;
|
||||
this.element = element = $(element);
|
||||
this.prepareOptions();
|
||||
this._controls = { };
|
||||
arguments.callee.dealWithDeprecatedOptions(options); // DEPRECATION LAYER!!!
|
||||
Object.extend(this.options, options || { });
|
||||
if (!this.options.formId && this.element.id) {
|
||||
this.options.formId = this.element.id + '-inplaceeditor';
|
||||
if ($(this.options.formId))
|
||||
this.options.formId = '';
|
||||
}
|
||||
if (this.options.externalControl)
|
||||
this.options.externalControl = $(this.options.externalControl);
|
||||
if (!this.options.externalControl)
|
||||
this.options.externalControlOnly = false;
|
||||
this._originalBackground = this.element.getStyle('background-color') || 'transparent';
|
||||
this.element.title = this.options.clickToEditText;
|
||||
this._boundCancelHandler = this.handleFormCancellation.bind(this);
|
||||
this._boundComplete = (this.options.onComplete || Prototype.emptyFunction).bind(this);
|
||||
this._boundFailureHandler = this.handleAJAXFailure.bind(this);
|
||||
this._boundSubmitHandler = this.handleFormSubmission.bind(this);
|
||||
this._boundWrapperHandler = this.wrapUp.bind(this);
|
||||
this.registerListeners();
|
||||
},
|
||||
checkForEscapeOrReturn: function(e) {
|
||||
if (!this._editing || e.ctrlKey || e.altKey || e.shiftKey) return;
|
||||
if (Event.KEY_ESC == e.keyCode)
|
||||
this.handleFormCancellation(e);
|
||||
else if (Event.KEY_RETURN == e.keyCode)
|
||||
this.handleFormSubmission(e);
|
||||
},
|
||||
createControl: function(mode, handler, extraClasses) {
|
||||
var control = this.options[mode + 'Control'];
|
||||
var text = this.options[mode + 'Text'];
|
||||
if ('button' == control) {
|
||||
var btn = document.createElement('input');
|
||||
btn.type = 'submit';
|
||||
btn.value = text;
|
||||
btn.className = 'editor_' + mode + '_button';
|
||||
if ('cancel' == mode)
|
||||
btn.onclick = this._boundCancelHandler;
|
||||
this._form.appendChild(btn);
|
||||
this._controls[mode] = btn;
|
||||
} else if ('link' == control) {
|
||||
var link = document.createElement('a');
|
||||
link.href = '#';
|
||||
link.appendChild(document.createTextNode(text));
|
||||
link.onclick = 'cancel' == mode ? this._boundCancelHandler : this._boundSubmitHandler;
|
||||
link.className = 'editor_' + mode + '_link';
|
||||
if (extraClasses)
|
||||
link.className += ' ' + extraClasses;
|
||||
this._form.appendChild(link);
|
||||
this._controls[mode] = link;
|
||||
}
|
||||
},
|
||||
createEditField: function() {
|
||||
var text = (this.options.loadTextURL ? this.options.loadingText : this.getText());
|
||||
var fld;
|
||||
if (1 >= this.options.rows && !/\r|\n/.test(this.getText())) {
|
||||
fld = document.createElement('input');
|
||||
fld.type = 'text';
|
||||
var size = this.options.size || this.options.cols || 0;
|
||||
if (0 < size) fld.size = size;
|
||||
} else {
|
||||
fld = document.createElement('textarea');
|
||||
fld.rows = (1 >= this.options.rows ? this.options.autoRows : this.options.rows);
|
||||
fld.cols = this.options.cols || 40;
|
||||
}
|
||||
fld.name = this.options.paramName;
|
||||
fld.value = text; // No HTML breaks conversion anymore
|
||||
fld.className = 'editor_field';
|
||||
if (this.options.submitOnBlur)
|
||||
fld.onblur = this._boundSubmitHandler;
|
||||
this._controls.editor = fld;
|
||||
if (this.options.loadTextURL)
|
||||
this.loadExternalText();
|
||||
this._form.appendChild(this._controls.editor);
|
||||
},
|
||||
createForm: function() {
|
||||
var ipe = this;
|
||||
function addText(mode, condition) {
|
||||
var text = ipe.options['text' + mode + 'Controls'];
|
||||
if (!text || condition === false) return;
|
||||
ipe._form.appendChild(document.createTextNode(text));
|
||||
};
|
||||
this._form = $(document.createElement('form'));
|
||||
this._form.id = this.options.formId;
|
||||
this._form.addClassName(this.options.formClassName);
|
||||
this._form.onsubmit = this._boundSubmitHandler;
|
||||
this.createEditField();
|
||||
if ('textarea' == this._controls.editor.tagName.toLowerCase())
|
||||
this._form.appendChild(document.createElement('br'));
|
||||
if (this.options.onFormCustomization)
|
||||
this.options.onFormCustomization(this, this._form);
|
||||
addText('Before', this.options.okControl || this.options.cancelControl);
|
||||
this.createControl('ok', this._boundSubmitHandler);
|
||||
addText('Between', this.options.okControl && this.options.cancelControl);
|
||||
this.createControl('cancel', this._boundCancelHandler, 'editor_cancel');
|
||||
addText('After', this.options.okControl || this.options.cancelControl);
|
||||
},
|
||||
destroy: function() {
|
||||
if (this._oldInnerHTML)
|
||||
this.element.innerHTML = this._oldInnerHTML;
|
||||
this.leaveEditMode();
|
||||
this.unregisterListeners();
|
||||
},
|
||||
enterEditMode: function(e) {
|
||||
if (this._saving || this._editing) return;
|
||||
this._editing = true;
|
||||
this.triggerCallback('onEnterEditMode');
|
||||
if (this.options.externalControl)
|
||||
this.options.externalControl.hide();
|
||||
this.element.hide();
|
||||
this.createForm();
|
||||
this.element.parentNode.insertBefore(this._form, this.element);
|
||||
if (!this.options.loadTextURL)
|
||||
this.postProcessEditField();
|
||||
if (e) Event.stop(e);
|
||||
},
|
||||
enterHover: function(e) {
|
||||
if (this.options.hoverClassName)
|
||||
this.element.addClassName(this.options.hoverClassName);
|
||||
if (this._saving) return;
|
||||
this.triggerCallback('onEnterHover');
|
||||
},
|
||||
getText: function() {
|
||||
return this.element.innerHTML;
|
||||
},
|
||||
handleAJAXFailure: function(transport) {
|
||||
this.triggerCallback('onFailure', transport);
|
||||
if (this._oldInnerHTML) {
|
||||
this.element.innerHTML = this._oldInnerHTML;
|
||||
this._oldInnerHTML = null;
|
||||
}
|
||||
},
|
||||
handleFormCancellation: function(e) {
|
||||
this.wrapUp();
|
||||
if (e) Event.stop(e);
|
||||
},
|
||||
handleFormSubmission: function(e) {
|
||||
var form = this._form;
|
||||
var value = $F(this._controls.editor);
|
||||
this.prepareSubmission();
|
||||
var params = this.options.callback(form, value) || '';
|
||||
if (Object.isString(params))
|
||||
params = params.toQueryParams();
|
||||
params.editorId = this.element.id;
|
||||
if (this.options.htmlResponse) {
|
||||
var options = Object.extend({ evalScripts: true }, this.options.ajaxOptions);
|
||||
Object.extend(options, {
|
||||
parameters: params,
|
||||
onComplete: this._boundWrapperHandler,
|
||||
onFailure: this._boundFailureHandler
|
||||
});
|
||||
new Ajax.Updater({ success: this.element }, this.url, options);
|
||||
} else {
|
||||
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
||||
Object.extend(options, {
|
||||
parameters: params,
|
||||
onComplete: this._boundWrapperHandler,
|
||||
onFailure: this._boundFailureHandler
|
||||
});
|
||||
new Ajax.Request(this.url, options);
|
||||
}
|
||||
if (e) Event.stop(e);
|
||||
},
|
||||
leaveEditMode: function() {
|
||||
this.element.removeClassName(this.options.savingClassName);
|
||||
this.removeForm();
|
||||
this.leaveHover();
|
||||
this.element.style.backgroundColor = this._originalBackground;
|
||||
this.element.show();
|
||||
if (this.options.externalControl)
|
||||
this.options.externalControl.show();
|
||||
this._saving = false;
|
||||
this._editing = false;
|
||||
this._oldInnerHTML = null;
|
||||
this.triggerCallback('onLeaveEditMode');
|
||||
},
|
||||
leaveHover: function(e) {
|
||||
if (this.options.hoverClassName)
|
||||
this.element.removeClassName(this.options.hoverClassName);
|
||||
if (this._saving) return;
|
||||
this.triggerCallback('onLeaveHover');
|
||||
},
|
||||
loadExternalText: function() {
|
||||
this._form.addClassName(this.options.loadingClassName);
|
||||
this._controls.editor.disabled = true;
|
||||
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
||||
Object.extend(options, {
|
||||
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
||||
onComplete: Prototype.emptyFunction,
|
||||
onSuccess: function(transport) {
|
||||
this._form.removeClassName(this.options.loadingClassName);
|
||||
var text = transport.responseText;
|
||||
if (this.options.stripLoadedTextTags)
|
||||
text = text.stripTags();
|
||||
this._controls.editor.value = text;
|
||||
this._controls.editor.disabled = false;
|
||||
this.postProcessEditField();
|
||||
}.bind(this),
|
||||
onFailure: this._boundFailureHandler
|
||||
});
|
||||
new Ajax.Request(this.options.loadTextURL, options);
|
||||
},
|
||||
postProcessEditField: function() {
|
||||
var fpc = this.options.fieldPostCreation;
|
||||
if (fpc)
|
||||
$(this._controls.editor)['focus' == fpc ? 'focus' : 'activate']();
|
||||
},
|
||||
prepareOptions: function() {
|
||||
this.options = Object.clone(Ajax.InPlaceEditor.DefaultOptions);
|
||||
Object.extend(this.options, Ajax.InPlaceEditor.DefaultCallbacks);
|
||||
[this._extraDefaultOptions].flatten().compact().each(function(defs) {
|
||||
Object.extend(this.options, defs);
|
||||
}.bind(this));
|
||||
},
|
||||
prepareSubmission: function() {
|
||||
this._saving = true;
|
||||
this.removeForm();
|
||||
this.leaveHover();
|
||||
this.showSaving();
|
||||
},
|
||||
registerListeners: function() {
|
||||
this._listeners = { };
|
||||
var listener;
|
||||
$H(Ajax.InPlaceEditor.Listeners).each(function(pair) {
|
||||
listener = this[pair.value].bind(this);
|
||||
this._listeners[pair.key] = listener;
|
||||
if (!this.options.externalControlOnly)
|
||||
this.element.observe(pair.key, listener);
|
||||
if (this.options.externalControl)
|
||||
this.options.externalControl.observe(pair.key, listener);
|
||||
}.bind(this));
|
||||
},
|
||||
removeForm: function() {
|
||||
if (!this._form) return;
|
||||
this._form.remove();
|
||||
this._form = null;
|
||||
this._controls = { };
|
||||
},
|
||||
showSaving: function() {
|
||||
this._oldInnerHTML = this.element.innerHTML;
|
||||
this.element.innerHTML = this.options.savingText;
|
||||
this.element.addClassName(this.options.savingClassName);
|
||||
this.element.style.backgroundColor = this._originalBackground;
|
||||
this.element.show();
|
||||
},
|
||||
triggerCallback: function(cbName, arg) {
|
||||
if ('function' == typeof this.options[cbName]) {
|
||||
this.options[cbName](this, arg);
|
||||
}
|
||||
},
|
||||
unregisterListeners: function() {
|
||||
$H(this._listeners).each(function(pair) {
|
||||
if (!this.options.externalControlOnly)
|
||||
this.element.stopObserving(pair.key, pair.value);
|
||||
if (this.options.externalControl)
|
||||
this.options.externalControl.stopObserving(pair.key, pair.value);
|
||||
}.bind(this));
|
||||
},
|
||||
wrapUp: function(transport) {
|
||||
this.leaveEditMode();
|
||||
// Can't use triggerCallback due to backward compatibility: requires
|
||||
// binding + direct element
|
||||
this._boundComplete(transport, this.element);
|
||||
}
|
||||
});
|
||||
|
||||
Object.extend(Ajax.InPlaceEditor.prototype, {
|
||||
dispose: Ajax.InPlaceEditor.prototype.destroy
|
||||
});
|
||||
|
||||
Ajax.InPlaceCollectionEditor = Class.create(Ajax.InPlaceEditor, {
|
||||
initialize: function($super, element, url, options) {
|
||||
this._extraDefaultOptions = Ajax.InPlaceCollectionEditor.DefaultOptions;
|
||||
$super(element, url, options);
|
||||
},
|
||||
|
||||
createEditField: function() {
|
||||
var list = document.createElement('select');
|
||||
list.name = this.options.paramName;
|
||||
list.size = 1;
|
||||
this._controls.editor = list;
|
||||
this._collection = this.options.collection || [];
|
||||
if (this.options.loadCollectionURL)
|
||||
this.loadCollection();
|
||||
else
|
||||
this.checkForExternalText();
|
||||
this._form.appendChild(this._controls.editor);
|
||||
},
|
||||
|
||||
loadCollection: function() {
|
||||
this._form.addClassName(this.options.loadingClassName);
|
||||
this.showLoadingText(this.options.loadingCollectionText);
|
||||
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
||||
Object.extend(options, {
|
||||
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
||||
onComplete: Prototype.emptyFunction,
|
||||
onSuccess: function(transport) {
|
||||
var js = transport.responseText.strip();
|
||||
if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
|
||||
throw 'Server returned an invalid collection representation.';
|
||||
this._collection = eval(js);
|
||||
this.checkForExternalText();
|
||||
}.bind(this),
|
||||
onFailure: this.onFailure
|
||||
});
|
||||
new Ajax.Request(this.options.loadCollectionURL, options);
|
||||
},
|
||||
|
||||
showLoadingText: function(text) {
|
||||
this._controls.editor.disabled = true;
|
||||
var tempOption = this._controls.editor.firstChild;
|
||||
if (!tempOption) {
|
||||
tempOption = document.createElement('option');
|
||||
tempOption.value = '';
|
||||
this._controls.editor.appendChild(tempOption);
|
||||
tempOption.selected = true;
|
||||
}
|
||||
tempOption.update((text || '').stripScripts().stripTags());
|
||||
},
|
||||
|
||||
checkForExternalText: function() {
|
||||
this._text = this.getText();
|
||||
if (this.options.loadTextURL)
|
||||
this.loadExternalText();
|
||||
else
|
||||
this.buildOptionList();
|
||||
},
|
||||
|
||||
loadExternalText: function() {
|
||||
this.showLoadingText(this.options.loadingText);
|
||||
var options = Object.extend({ method: 'get' }, this.options.ajaxOptions);
|
||||
Object.extend(options, {
|
||||
parameters: 'editorId=' + encodeURIComponent(this.element.id),
|
||||
onComplete: Prototype.emptyFunction,
|
||||
onSuccess: function(transport) {
|
||||
this._text = transport.responseText.strip();
|
||||
this.buildOptionList();
|
||||
}.bind(this),
|
||||
onFailure: this.onFailure
|
||||
});
|
||||
new Ajax.Request(this.options.loadTextURL, options);
|
||||
},
|
||||
|
||||
buildOptionList: function() {
|
||||
this._form.removeClassName(this.options.loadingClassName);
|
||||
this._collection = this._collection.map(function(entry) {
|
||||
return 2 === entry.length ? entry : [entry, entry].flatten();
|
||||
});
|
||||
var marker = ('value' in this.options) ? this.options.value : this._text;
|
||||
var textFound = this._collection.any(function(entry) {
|
||||
return entry[0] == marker;
|
||||
}.bind(this));
|
||||
this._controls.editor.update('');
|
||||
var option;
|
||||
this._collection.each(function(entry, index) {
|
||||
option = document.createElement('option');
|
||||
option.value = entry[0];
|
||||
option.selected = textFound ? entry[0] == marker : 0 == index;
|
||||
option.appendChild(document.createTextNode(entry[1]));
|
||||
this._controls.editor.appendChild(option);
|
||||
}.bind(this));
|
||||
this._controls.editor.disabled = false;
|
||||
Field.scrollFreeActivate(this._controls.editor);
|
||||
}
|
||||
});
|
||||
|
||||
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
|
||||
//**** This only exists for a while, in order to let ****
|
||||
//**** users adapt to the new API. Read up on the new ****
|
||||
//**** API and convert your code to it ASAP! ****
|
||||
|
||||
Ajax.InPlaceEditor.prototype.initialize.dealWithDeprecatedOptions = function(options) {
|
||||
if (!options) return;
|
||||
function fallback(name, expr) {
|
||||
if (name in options || expr === undefined) return;
|
||||
options[name] = expr;
|
||||
};
|
||||
fallback('cancelControl', (options.cancelLink ? 'link' : (options.cancelButton ? 'button' :
|
||||
options.cancelLink == options.cancelButton == false ? false : undefined)));
|
||||
fallback('okControl', (options.okLink ? 'link' : (options.okButton ? 'button' :
|
||||
options.okLink == options.okButton == false ? false : undefined)));
|
||||
fallback('highlightColor', options.highlightcolor);
|
||||
fallback('highlightEndColor', options.highlightendcolor);
|
||||
};
|
||||
|
||||
Object.extend(Ajax.InPlaceEditor, {
|
||||
DefaultOptions: {
|
||||
ajaxOptions: { },
|
||||
autoRows: 3, // Use when multi-line w/ rows == 1
|
||||
cancelControl: 'link', // 'link'|'button'|false
|
||||
cancelText: 'cancel',
|
||||
clickToEditText: 'Click to edit',
|
||||
externalControl: null, // id|elt
|
||||
externalControlOnly: false,
|
||||
fieldPostCreation: 'activate', // 'activate'|'focus'|false
|
||||
formClassName: 'inplaceeditor-form',
|
||||
formId: null, // id|elt
|
||||
highlightColor: '#ffff99',
|
||||
highlightEndColor: '#ffffff',
|
||||
hoverClassName: '',
|
||||
htmlResponse: true,
|
||||
loadingClassName: 'inplaceeditor-loading',
|
||||
loadingText: 'Loading...',
|
||||
okControl: 'button', // 'link'|'button'|false
|
||||
okText: 'ok',
|
||||
paramName: 'value',
|
||||
rows: 1, // If 1 and multi-line, uses autoRows
|
||||
savingClassName: 'inplaceeditor-saving',
|
||||
savingText: 'Saving...',
|
||||
size: 0,
|
||||
stripLoadedTextTags: false,
|
||||
submitOnBlur: false,
|
||||
textAfterControls: '',
|
||||
textBeforeControls: '',
|
||||
textBetweenControls: ''
|
||||
},
|
||||
DefaultCallbacks: {
|
||||
callback: function(form) {
|
||||
return Form.serialize(form);
|
||||
},
|
||||
onComplete: function(transport, element) {
|
||||
// For backward compatibility, this one is bound to the IPE, and passes
|
||||
// the element directly. It was too often customized, so we don't break it.
|
||||
new Effect.Highlight(element, {
|
||||
startcolor: this.options.highlightColor, keepBackgroundImage: true });
|
||||
},
|
||||
onEnterEditMode: null,
|
||||
onEnterHover: function(ipe) {
|
||||
ipe.element.style.backgroundColor = ipe.options.highlightColor;
|
||||
if (ipe._effect)
|
||||
ipe._effect.cancel();
|
||||
},
|
||||
onFailure: function(transport, ipe) {
|
||||
alert('Error communication with the server: ' + transport.responseText.stripTags());
|
||||
},
|
||||
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
|
||||
onLeaveEditMode: null,
|
||||
onLeaveHover: function(ipe) {
|
||||
ipe._effect = new Effect.Highlight(ipe.element, {
|
||||
startcolor: ipe.options.highlightColor, endcolor: ipe.options.highlightEndColor,
|
||||
restorecolor: ipe._originalBackground, keepBackgroundImage: true
|
||||
});
|
||||
}
|
||||
},
|
||||
Listeners: {
|
||||
click: 'enterEditMode',
|
||||
keydown: 'checkForEscapeOrReturn',
|
||||
mouseover: 'enterHover',
|
||||
mouseout: 'leaveHover'
|
||||
}
|
||||
});
|
||||
|
||||
Ajax.InPlaceCollectionEditor.DefaultOptions = {
|
||||
loadingCollectionText: 'Loading options...'
|
||||
};
|
||||
|
||||
// Delayed observer, like Form.Element.Observer,
|
||||
// but waits for delay after last key input
|
||||
// Ideal for live-search fields
|
||||
|
||||
Form.Element.DelayedObserver = Class.create({
|
||||
initialize: function(element, delay, callback) {
|
||||
this.delay = delay || 0.5;
|
||||
this.element = $(element);
|
||||
this.callback = callback;
|
||||
this.timer = null;
|
||||
this.lastValue = $F(this.element);
|
||||
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
||||
},
|
||||
delayedListener: function(event) {
|
||||
if(this.lastValue == $F(this.element)) return;
|
||||
if(this.timer) clearTimeout(this.timer);
|
||||
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
||||
this.lastValue = $F(this.element);
|
||||
},
|
||||
onTimerEvent: function() {
|
||||
this.timer = null;
|
||||
this.callback(this.element, $F(this.element));
|
||||
}
|
||||
});
|
974
bbb-lti/web-app/js/prototype/dragdrop.js
vendored
@ -1,974 +0,0 @@
|
||||
// script.aculo.us dragdrop.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
// (c) 2005-2007 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
if(Object.isUndefined(Effect))
|
||||
throw("dragdrop.js requires including script.aculo.us' effects.js library");
|
||||
|
||||
var Droppables = {
|
||||
drops: [],
|
||||
|
||||
remove: function(element) {
|
||||
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
|
||||
},
|
||||
|
||||
add: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend({
|
||||
greedy: true,
|
||||
hoverclass: null,
|
||||
tree: false
|
||||
}, arguments[1] || { });
|
||||
|
||||
// cache containers
|
||||
if(options.containment) {
|
||||
options._containers = [];
|
||||
var containment = options.containment;
|
||||
if(Object.isArray(containment)) {
|
||||
containment.each( function(c) { options._containers.push($(c)) });
|
||||
} else {
|
||||
options._containers.push($(containment));
|
||||
}
|
||||
}
|
||||
|
||||
if(options.accept) options.accept = [options.accept].flatten();
|
||||
|
||||
Element.makePositioned(element); // fix IE
|
||||
options.element = element;
|
||||
|
||||
this.drops.push(options);
|
||||
},
|
||||
|
||||
findDeepestChild: function(drops) {
|
||||
deepest = drops[0];
|
||||
|
||||
for (i = 1; i < drops.length; ++i)
|
||||
if (Element.isParent(drops[i].element, deepest.element))
|
||||
deepest = drops[i];
|
||||
|
||||
return deepest;
|
||||
},
|
||||
|
||||
isContained: function(element, drop) {
|
||||
var containmentNode;
|
||||
if(drop.tree) {
|
||||
containmentNode = element.treeNode;
|
||||
} else {
|
||||
containmentNode = element.parentNode;
|
||||
}
|
||||
return drop._containers.detect(function(c) { return containmentNode == c });
|
||||
},
|
||||
|
||||
isAffected: function(point, element, drop) {
|
||||
return (
|
||||
(drop.element!=element) &&
|
||||
((!drop._containers) ||
|
||||
this.isContained(element, drop)) &&
|
||||
((!drop.accept) ||
|
||||
(Element.classNames(element).detect(
|
||||
function(v) { return drop.accept.include(v) } ) )) &&
|
||||
Position.within(drop.element, point[0], point[1]) );
|
||||
},
|
||||
|
||||
deactivate: function(drop) {
|
||||
if(drop.hoverclass)
|
||||
Element.removeClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = null;
|
||||
},
|
||||
|
||||
activate: function(drop) {
|
||||
if(drop.hoverclass)
|
||||
Element.addClassName(drop.element, drop.hoverclass);
|
||||
this.last_active = drop;
|
||||
},
|
||||
|
||||
show: function(point, element) {
|
||||
if(!this.drops.length) return;
|
||||
var drop, affected = [];
|
||||
|
||||
this.drops.each( function(drop) {
|
||||
if(Droppables.isAffected(point, element, drop))
|
||||
affected.push(drop);
|
||||
});
|
||||
|
||||
if(affected.length>0)
|
||||
drop = Droppables.findDeepestChild(affected);
|
||||
|
||||
if(this.last_active && this.last_active != drop) this.deactivate(this.last_active);
|
||||
if (drop) {
|
||||
Position.within(drop.element, point[0], point[1]);
|
||||
if(drop.onHover)
|
||||
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
||||
|
||||
if (drop != this.last_active) Droppables.activate(drop);
|
||||
}
|
||||
},
|
||||
|
||||
fire: function(event, element) {
|
||||
if(!this.last_active) return;
|
||||
Position.prepare();
|
||||
|
||||
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
||||
if (this.last_active.onDrop) {
|
||||
this.last_active.onDrop(element, this.last_active.element, event);
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
if(this.last_active)
|
||||
this.deactivate(this.last_active);
|
||||
}
|
||||
}
|
||||
|
||||
var Draggables = {
|
||||
drags: [],
|
||||
observers: [],
|
||||
|
||||
register: function(draggable) {
|
||||
if(this.drags.length == 0) {
|
||||
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
||||
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
||||
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
||||
|
||||
Event.observe(document, "mouseup", this.eventMouseUp);
|
||||
Event.observe(document, "mousemove", this.eventMouseMove);
|
||||
Event.observe(document, "keypress", this.eventKeypress);
|
||||
}
|
||||
this.drags.push(draggable);
|
||||
},
|
||||
|
||||
unregister: function(draggable) {
|
||||
this.drags = this.drags.reject(function(d) { return d==draggable });
|
||||
if(this.drags.length == 0) {
|
||||
Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
||||
Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
||||
Event.stopObserving(document, "keypress", this.eventKeypress);
|
||||
}
|
||||
},
|
||||
|
||||
activate: function(draggable) {
|
||||
if(draggable.options.delay) {
|
||||
this._timeout = setTimeout(function() {
|
||||
Draggables._timeout = null;
|
||||
window.focus();
|
||||
Draggables.activeDraggable = draggable;
|
||||
}.bind(this), draggable.options.delay);
|
||||
} else {
|
||||
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
||||
this.activeDraggable = draggable;
|
||||
}
|
||||
},
|
||||
|
||||
deactivate: function() {
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
updateDrag: function(event) {
|
||||
if(!this.activeDraggable) return;
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
// Mozilla-based browsers fire successive mousemove events with
|
||||
// the same coordinates, prevent needless redrawing (moz bug?)
|
||||
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
||||
this._lastPointer = pointer;
|
||||
|
||||
this.activeDraggable.updateDrag(event, pointer);
|
||||
},
|
||||
|
||||
endDrag: function(event) {
|
||||
if(this._timeout) {
|
||||
clearTimeout(this._timeout);
|
||||
this._timeout = null;
|
||||
}
|
||||
if(!this.activeDraggable) return;
|
||||
this._lastPointer = null;
|
||||
this.activeDraggable.endDrag(event);
|
||||
this.activeDraggable = null;
|
||||
},
|
||||
|
||||
keyPress: function(event) {
|
||||
if(this.activeDraggable)
|
||||
this.activeDraggable.keyPress(event);
|
||||
},
|
||||
|
||||
addObserver: function(observer) {
|
||||
this.observers.push(observer);
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
|
||||
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
||||
this.observers = this.observers.reject( function(o) { return o.element==element });
|
||||
this._cacheObserverCallbacks();
|
||||
},
|
||||
|
||||
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
||||
if(this[eventName+'Count'] > 0)
|
||||
this.observers.each( function(o) {
|
||||
if(o[eventName]) o[eventName](eventName, draggable, event);
|
||||
});
|
||||
if(draggable.options[eventName]) draggable.options[eventName](draggable, event);
|
||||
},
|
||||
|
||||
_cacheObserverCallbacks: function() {
|
||||
['onStart','onEnd','onDrag'].each( function(eventName) {
|
||||
Draggables[eventName+'Count'] = Draggables.observers.select(
|
||||
function(o) { return o[eventName]; }
|
||||
).length;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var Draggable = Class.create({
|
||||
initialize: function(element) {
|
||||
var defaults = {
|
||||
handle: false,
|
||||
reverteffect: function(element, top_offset, left_offset) {
|
||||
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
|
||||
new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur,
|
||||
queue: {scope:'_draggable', position:'end'}
|
||||
});
|
||||
},
|
||||
endeffect: function(element) {
|
||||
var toOpacity = Object.isNumber(element._opacity) ? element._opacity : 1.0;
|
||||
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity,
|
||||
queue: {scope:'_draggable', position:'end'},
|
||||
afterFinish: function(){
|
||||
Draggable._dragging[element] = false
|
||||
}
|
||||
});
|
||||
},
|
||||
zindex: 1000,
|
||||
revert: false,
|
||||
quiet: false,
|
||||
scroll: false,
|
||||
scrollSensitivity: 20,
|
||||
scrollSpeed: 15,
|
||||
snap: false, // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
||||
delay: 0
|
||||
};
|
||||
|
||||
if(!arguments[1] || Object.isUndefined(arguments[1].endeffect))
|
||||
Object.extend(defaults, {
|
||||
starteffect: function(element) {
|
||||
element._opacity = Element.getOpacity(element);
|
||||
Draggable._dragging[element] = true;
|
||||
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
|
||||
}
|
||||
});
|
||||
|
||||
var options = Object.extend(defaults, arguments[1] || { });
|
||||
|
||||
this.element = $(element);
|
||||
|
||||
if(options.handle && Object.isString(options.handle))
|
||||
this.handle = this.element.down('.'+options.handle, 0);
|
||||
|
||||
if(!this.handle) this.handle = $(options.handle);
|
||||
if(!this.handle) this.handle = this.element;
|
||||
|
||||
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML) {
|
||||
options.scroll = $(options.scroll);
|
||||
this._isScrollChild = Element.childOf(this.element, options.scroll);
|
||||
}
|
||||
|
||||
Element.makePositioned(this.element); // fix IE
|
||||
|
||||
this.options = options;
|
||||
this.dragging = false;
|
||||
|
||||
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
||||
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
||||
|
||||
Draggables.register(this);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
||||
Draggables.unregister(this);
|
||||
},
|
||||
|
||||
currentDelta: function() {
|
||||
return([
|
||||
parseInt(Element.getStyle(this.element,'left') || '0'),
|
||||
parseInt(Element.getStyle(this.element,'top') || '0')]);
|
||||
},
|
||||
|
||||
initDrag: function(event) {
|
||||
if(!Object.isUndefined(Draggable._dragging[this.element]) &&
|
||||
Draggable._dragging[this.element]) return;
|
||||
if(Event.isLeftClick(event)) {
|
||||
// abort on form elements, fixes a Firefox issue
|
||||
var src = Event.element(event);
|
||||
if((tag_name = src.tagName.toUpperCase()) && (
|
||||
tag_name=='INPUT' ||
|
||||
tag_name=='SELECT' ||
|
||||
tag_name=='OPTION' ||
|
||||
tag_name=='BUTTON' ||
|
||||
tag_name=='TEXTAREA')) return;
|
||||
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
var pos = Position.cumulativeOffset(this.element);
|
||||
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
||||
|
||||
Draggables.activate(this);
|
||||
Event.stop(event);
|
||||
}
|
||||
},
|
||||
|
||||
startDrag: function(event) {
|
||||
this.dragging = true;
|
||||
if(!this.delta)
|
||||
this.delta = this.currentDelta();
|
||||
|
||||
if(this.options.zindex) {
|
||||
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
||||
this.element.style.zIndex = this.options.zindex;
|
||||
}
|
||||
|
||||
if(this.options.ghosting) {
|
||||
this._clone = this.element.cloneNode(true);
|
||||
this.element._originallyAbsolute = (this.element.getStyle('position') == 'absolute');
|
||||
if (!this.element._originallyAbsolute)
|
||||
Position.absolutize(this.element);
|
||||
this.element.parentNode.insertBefore(this._clone, this.element);
|
||||
}
|
||||
|
||||
if(this.options.scroll) {
|
||||
if (this.options.scroll == window) {
|
||||
var where = this._getWindowScroll(this.options.scroll);
|
||||
this.originalScrollLeft = where.left;
|
||||
this.originalScrollTop = where.top;
|
||||
} else {
|
||||
this.originalScrollLeft = this.options.scroll.scrollLeft;
|
||||
this.originalScrollTop = this.options.scroll.scrollTop;
|
||||
}
|
||||
}
|
||||
|
||||
Draggables.notify('onStart', this, event);
|
||||
|
||||
if(this.options.starteffect) this.options.starteffect(this.element);
|
||||
},
|
||||
|
||||
updateDrag: function(event, pointer) {
|
||||
if(!this.dragging) this.startDrag(event);
|
||||
|
||||
if(!this.options.quiet){
|
||||
Position.prepare();
|
||||
Droppables.show(pointer, this.element);
|
||||
}
|
||||
|
||||
Draggables.notify('onDrag', this, event);
|
||||
|
||||
this.draw(pointer);
|
||||
if(this.options.change) this.options.change(this);
|
||||
|
||||
if(this.options.scroll) {
|
||||
this.stopScrolling();
|
||||
|
||||
var p;
|
||||
if (this.options.scroll == window) {
|
||||
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
|
||||
} else {
|
||||
p = Position.page(this.options.scroll);
|
||||
p[0] += this.options.scroll.scrollLeft + Position.deltaX;
|
||||
p[1] += this.options.scroll.scrollTop + Position.deltaY;
|
||||
p.push(p[0]+this.options.scroll.offsetWidth);
|
||||
p.push(p[1]+this.options.scroll.offsetHeight);
|
||||
}
|
||||
var speed = [0,0];
|
||||
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
|
||||
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
|
||||
if(pointer[0] > (p[2]-this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[2]-this.options.scrollSensitivity);
|
||||
if(pointer[1] > (p[3]-this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[3]-this.options.scrollSensitivity);
|
||||
this.startScrolling(speed);
|
||||
}
|
||||
|
||||
// fix AppleWebKit rendering
|
||||
if(Prototype.Browser.WebKit) window.scrollBy(0,0);
|
||||
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
finishDrag: function(event, success) {
|
||||
this.dragging = false;
|
||||
|
||||
if(this.options.quiet){
|
||||
Position.prepare();
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
Droppables.show(pointer, this.element);
|
||||
}
|
||||
|
||||
if(this.options.ghosting) {
|
||||
if (!this.element._originallyAbsolute)
|
||||
Position.relativize(this.element);
|
||||
delete this.element._originallyAbsolute;
|
||||
Element.remove(this._clone);
|
||||
this._clone = null;
|
||||
}
|
||||
|
||||
var dropped = false;
|
||||
if(success) {
|
||||
dropped = Droppables.fire(event, this.element);
|
||||
if (!dropped) dropped = false;
|
||||
}
|
||||
if(dropped && this.options.onDropped) this.options.onDropped(this.element);
|
||||
Draggables.notify('onEnd', this, event);
|
||||
|
||||
var revert = this.options.revert;
|
||||
if(revert && Object.isFunction(revert)) revert = revert(this.element);
|
||||
|
||||
var d = this.currentDelta();
|
||||
if(revert && this.options.reverteffect) {
|
||||
if (dropped == 0 || revert != 'failure')
|
||||
this.options.reverteffect(this.element,
|
||||
d[1]-this.delta[1], d[0]-this.delta[0]);
|
||||
} else {
|
||||
this.delta = d;
|
||||
}
|
||||
|
||||
if(this.options.zindex)
|
||||
this.element.style.zIndex = this.originalZ;
|
||||
|
||||
if(this.options.endeffect)
|
||||
this.options.endeffect(this.element);
|
||||
|
||||
Draggables.deactivate(this);
|
||||
Droppables.reset();
|
||||
},
|
||||
|
||||
keyPress: function(event) {
|
||||
if(event.keyCode!=Event.KEY_ESC) return;
|
||||
this.finishDrag(event, false);
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
endDrag: function(event) {
|
||||
if(!this.dragging) return;
|
||||
this.stopScrolling();
|
||||
this.finishDrag(event, true);
|
||||
Event.stop(event);
|
||||
},
|
||||
|
||||
draw: function(point) {
|
||||
var pos = Position.cumulativeOffset(this.element);
|
||||
if(this.options.ghosting) {
|
||||
var r = Position.realOffset(this.element);
|
||||
pos[0] += r[0] - Position.deltaX; pos[1] += r[1] - Position.deltaY;
|
||||
}
|
||||
|
||||
var d = this.currentDelta();
|
||||
pos[0] -= d[0]; pos[1] -= d[1];
|
||||
|
||||
if(this.options.scroll && (this.options.scroll != window && this._isScrollChild)) {
|
||||
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
|
||||
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
|
||||
}
|
||||
|
||||
var p = [0,1].map(function(i){
|
||||
return (point[i]-pos[i]-this.offset[i])
|
||||
}.bind(this));
|
||||
|
||||
if(this.options.snap) {
|
||||
if(Object.isFunction(this.options.snap)) {
|
||||
p = this.options.snap(p[0],p[1],this);
|
||||
} else {
|
||||
if(Object.isArray(this.options.snap)) {
|
||||
p = p.map( function(v, i) {
|
||||
return (v/this.options.snap[i]).round()*this.options.snap[i] }.bind(this))
|
||||
} else {
|
||||
p = p.map( function(v) {
|
||||
return (v/this.options.snap).round()*this.options.snap }.bind(this))
|
||||
}
|
||||
}}
|
||||
|
||||
var style = this.element.style;
|
||||
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
||||
style.left = p[0] + "px";
|
||||
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
||||
style.top = p[1] + "px";
|
||||
|
||||
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
||||
},
|
||||
|
||||
stopScrolling: function() {
|
||||
if(this.scrollInterval) {
|
||||
clearInterval(this.scrollInterval);
|
||||
this.scrollInterval = null;
|
||||
Draggables._lastScrollPointer = null;
|
||||
}
|
||||
},
|
||||
|
||||
startScrolling: function(speed) {
|
||||
if(!(speed[0] || speed[1])) return;
|
||||
this.scrollSpeed = [speed[0]*this.options.scrollSpeed,speed[1]*this.options.scrollSpeed];
|
||||
this.lastScrolled = new Date();
|
||||
this.scrollInterval = setInterval(this.scroll.bind(this), 10);
|
||||
},
|
||||
|
||||
scroll: function() {
|
||||
var current = new Date();
|
||||
var delta = current - this.lastScrolled;
|
||||
this.lastScrolled = current;
|
||||
if(this.options.scroll == window) {
|
||||
with (this._getWindowScroll(this.options.scroll)) {
|
||||
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
|
||||
var d = delta / 1000;
|
||||
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
||||
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
||||
}
|
||||
|
||||
Position.prepare();
|
||||
Droppables.show(Draggables._lastPointer, this.element);
|
||||
Draggables.notify('onDrag', this);
|
||||
if (this._isScrollChild) {
|
||||
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
|
||||
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
|
||||
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
|
||||
if (Draggables._lastScrollPointer[0] < 0)
|
||||
Draggables._lastScrollPointer[0] = 0;
|
||||
if (Draggables._lastScrollPointer[1] < 0)
|
||||
Draggables._lastScrollPointer[1] = 0;
|
||||
this.draw(Draggables._lastScrollPointer);
|
||||
}
|
||||
|
||||
if(this.options.change) this.options.change(this);
|
||||
},
|
||||
|
||||
_getWindowScroll: function(w) {
|
||||
var T, L, W, H;
|
||||
with (w.document) {
|
||||
if (w.document.documentElement && documentElement.scrollTop) {
|
||||
T = documentElement.scrollTop;
|
||||
L = documentElement.scrollLeft;
|
||||
} else if (w.document.body) {
|
||||
T = body.scrollTop;
|
||||
L = body.scrollLeft;
|
||||
}
|
||||
if (w.innerWidth) {
|
||||
W = w.innerWidth;
|
||||
H = w.innerHeight;
|
||||
} else if (w.document.documentElement && documentElement.clientWidth) {
|
||||
W = documentElement.clientWidth;
|
||||
H = documentElement.clientHeight;
|
||||
} else {
|
||||
W = body.offsetWidth;
|
||||
H = body.offsetHeight
|
||||
}
|
||||
}
|
||||
return { top: T, left: L, width: W, height: H };
|
||||
}
|
||||
});
|
||||
|
||||
Draggable._dragging = { };
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
var SortableObserver = Class.create({
|
||||
initialize: function(element, observer) {
|
||||
this.element = $(element);
|
||||
this.observer = observer;
|
||||
this.lastValue = Sortable.serialize(this.element);
|
||||
},
|
||||
|
||||
onStart: function() {
|
||||
this.lastValue = Sortable.serialize(this.element);
|
||||
},
|
||||
|
||||
onEnd: function() {
|
||||
Sortable.unmark();
|
||||
if(this.lastValue != Sortable.serialize(this.element))
|
||||
this.observer(this.element)
|
||||
}
|
||||
});
|
||||
|
||||
var Sortable = {
|
||||
SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
|
||||
|
||||
sortables: { },
|
||||
|
||||
_findRootElement: function(element) {
|
||||
while (element.tagName.toUpperCase() != "BODY") {
|
||||
if(element.id && Sortable.sortables[element.id]) return element;
|
||||
element = element.parentNode;
|
||||
}
|
||||
},
|
||||
|
||||
options: function(element) {
|
||||
element = Sortable._findRootElement($(element));
|
||||
if(!element) return;
|
||||
return Sortable.sortables[element.id];
|
||||
},
|
||||
|
||||
destroy: function(element){
|
||||
var s = Sortable.options(element);
|
||||
|
||||
if(s) {
|
||||
Draggables.removeObserver(s.element);
|
||||
s.droppables.each(function(d){ Droppables.remove(d) });
|
||||
s.draggables.invoke('destroy');
|
||||
|
||||
delete Sortable.sortables[s.element.id];
|
||||
}
|
||||
},
|
||||
|
||||
create: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend({
|
||||
element: element,
|
||||
tag: 'li', // assumes li children, override with tag: 'tagname'
|
||||
dropOnEmpty: false,
|
||||
tree: false,
|
||||
treeTag: 'ul',
|
||||
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
||||
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
||||
containment: element, // also takes array of elements (or id's); or false
|
||||
handle: false, // or a CSS class
|
||||
only: false,
|
||||
delay: 0,
|
||||
hoverclass: null,
|
||||
ghosting: false,
|
||||
quiet: false,
|
||||
scroll: false,
|
||||
scrollSensitivity: 20,
|
||||
scrollSpeed: 15,
|
||||
format: this.SERIALIZE_RULE,
|
||||
|
||||
// these take arrays of elements or ids and can be
|
||||
// used for better initialization performance
|
||||
elements: false,
|
||||
handles: false,
|
||||
|
||||
onChange: Prototype.emptyFunction,
|
||||
onUpdate: Prototype.emptyFunction
|
||||
}, arguments[1] || { });
|
||||
|
||||
// clear any old sortable with same element
|
||||
this.destroy(element);
|
||||
|
||||
// build options for the draggables
|
||||
var options_for_draggable = {
|
||||
revert: true,
|
||||
quiet: options.quiet,
|
||||
scroll: options.scroll,
|
||||
scrollSpeed: options.scrollSpeed,
|
||||
scrollSensitivity: options.scrollSensitivity,
|
||||
delay: options.delay,
|
||||
ghosting: options.ghosting,
|
||||
constraint: options.constraint,
|
||||
handle: options.handle };
|
||||
|
||||
if(options.starteffect)
|
||||
options_for_draggable.starteffect = options.starteffect;
|
||||
|
||||
if(options.reverteffect)
|
||||
options_for_draggable.reverteffect = options.reverteffect;
|
||||
else
|
||||
if(options.ghosting) options_for_draggable.reverteffect = function(element) {
|
||||
element.style.top = 0;
|
||||
element.style.left = 0;
|
||||
};
|
||||
|
||||
if(options.endeffect)
|
||||
options_for_draggable.endeffect = options.endeffect;
|
||||
|
||||
if(options.zindex)
|
||||
options_for_draggable.zindex = options.zindex;
|
||||
|
||||
// build options for the droppables
|
||||
var options_for_droppable = {
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
tree: options.tree,
|
||||
hoverclass: options.hoverclass,
|
||||
onHover: Sortable.onHover
|
||||
}
|
||||
|
||||
var options_for_tree = {
|
||||
onHover: Sortable.onEmptyHover,
|
||||
overlap: options.overlap,
|
||||
containment: options.containment,
|
||||
hoverclass: options.hoverclass
|
||||
}
|
||||
|
||||
// fix for gecko engine
|
||||
Element.cleanWhitespace(element);
|
||||
|
||||
options.draggables = [];
|
||||
options.droppables = [];
|
||||
|
||||
// drop on empty handling
|
||||
if(options.dropOnEmpty || options.tree) {
|
||||
Droppables.add(element, options_for_tree);
|
||||
options.droppables.push(element);
|
||||
}
|
||||
|
||||
(options.elements || this.findElements(element, options) || []).each( function(e,i) {
|
||||
var handle = options.handles ? $(options.handles[i]) :
|
||||
(options.handle ? $(e).select('.' + options.handle)[0] : e);
|
||||
options.draggables.push(
|
||||
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
||||
Droppables.add(e, options_for_droppable);
|
||||
if(options.tree) e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
|
||||
if(options.tree) {
|
||||
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
||||
Droppables.add(e, options_for_tree);
|
||||
e.treeNode = element;
|
||||
options.droppables.push(e);
|
||||
});
|
||||
}
|
||||
|
||||
// keep reference
|
||||
this.sortables[element.id] = options;
|
||||
|
||||
// for onupdate
|
||||
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
|
||||
|
||||
},
|
||||
|
||||
// return all suitable-for-sortable elements in a guaranteed order
|
||||
findElements: function(element, options) {
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.tag);
|
||||
},
|
||||
|
||||
findTreeElements: function(element, options) {
|
||||
return Element.findChildren(
|
||||
element, options.only, options.tree ? true : false, options.treeTag);
|
||||
},
|
||||
|
||||
onHover: function(element, dropon, overlap) {
|
||||
if(Element.isParent(dropon, element)) return;
|
||||
|
||||
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
|
||||
return;
|
||||
} else if(overlap>0.5) {
|
||||
Sortable.mark(dropon, 'before');
|
||||
if(dropon.previousSibling != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = "hidden"; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, dropon);
|
||||
if(dropon.parentNode!=oldParentNode)
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
Sortable.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
} else {
|
||||
Sortable.mark(dropon, 'after');
|
||||
var nextElement = dropon.nextSibling || null;
|
||||
if(nextElement != element) {
|
||||
var oldParentNode = element.parentNode;
|
||||
element.style.visibility = "hidden"; // fix gecko rendering
|
||||
dropon.parentNode.insertBefore(element, nextElement);
|
||||
if(dropon.parentNode!=oldParentNode)
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
Sortable.options(dropon.parentNode).onChange(element);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onEmptyHover: function(element, dropon, overlap) {
|
||||
var oldParentNode = element.parentNode;
|
||||
var droponOptions = Sortable.options(dropon);
|
||||
|
||||
if(!Element.isParent(dropon, element)) {
|
||||
var index;
|
||||
|
||||
var children = Sortable.findElements(dropon, {tag: droponOptions.tag, only: droponOptions.only});
|
||||
var child = null;
|
||||
|
||||
if(children) {
|
||||
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
||||
|
||||
for (index = 0; index < children.length; index += 1) {
|
||||
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
||||
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
||||
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
|
||||
child = index + 1 < children.length ? children[index + 1] : null;
|
||||
break;
|
||||
} else {
|
||||
child = children[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dropon.insertBefore(element, child);
|
||||
|
||||
Sortable.options(oldParentNode).onChange(element);
|
||||
droponOptions.onChange(element);
|
||||
}
|
||||
},
|
||||
|
||||
unmark: function() {
|
||||
if(Sortable._marker) Sortable._marker.hide();
|
||||
},
|
||||
|
||||
mark: function(dropon, position) {
|
||||
// mark on ghosting only
|
||||
var sortable = Sortable.options(dropon.parentNode);
|
||||
if(sortable && !sortable.ghosting) return;
|
||||
|
||||
if(!Sortable._marker) {
|
||||
Sortable._marker =
|
||||
($('dropmarker') || Element.extend(document.createElement('DIV'))).
|
||||
hide().addClassName('dropmarker').setStyle({position:'absolute'});
|
||||
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
||||
}
|
||||
var offsets = Position.cumulativeOffset(dropon);
|
||||
Sortable._marker.setStyle({left: offsets[0]+'px', top: offsets[1] + 'px'});
|
||||
|
||||
if(position=='after')
|
||||
if(sortable.overlap == 'horizontal')
|
||||
Sortable._marker.setStyle({left: (offsets[0]+dropon.clientWidth) + 'px'});
|
||||
else
|
||||
Sortable._marker.setStyle({top: (offsets[1]+dropon.clientHeight) + 'px'});
|
||||
|
||||
Sortable._marker.show();
|
||||
},
|
||||
|
||||
_tree: function(element, options, parent) {
|
||||
var children = Sortable.findElements(element, options) || [];
|
||||
|
||||
for (var i = 0; i < children.length; ++i) {
|
||||
var match = children[i].id.match(options.format);
|
||||
|
||||
if (!match) continue;
|
||||
|
||||
var child = {
|
||||
id: encodeURIComponent(match ? match[1] : null),
|
||||
element: element,
|
||||
parent: parent,
|
||||
children: [],
|
||||
position: parent.children.length,
|
||||
container: $(children[i]).down(options.treeTag)
|
||||
}
|
||||
|
||||
/* Get the element containing the children and recurse over it */
|
||||
if (child.container)
|
||||
this._tree(child.container, options, child)
|
||||
|
||||
parent.children.push (child);
|
||||
}
|
||||
|
||||
return parent;
|
||||
},
|
||||
|
||||
tree: function(element) {
|
||||
element = $(element);
|
||||
var sortableOptions = this.options(element);
|
||||
var options = Object.extend({
|
||||
tag: sortableOptions.tag,
|
||||
treeTag: sortableOptions.treeTag,
|
||||
only: sortableOptions.only,
|
||||
name: element.id,
|
||||
format: sortableOptions.format
|
||||
}, arguments[1] || { });
|
||||
|
||||
var root = {
|
||||
id: null,
|
||||
parent: null,
|
||||
children: [],
|
||||
container: element,
|
||||
position: 0
|
||||
}
|
||||
|
||||
return Sortable._tree(element, options, root);
|
||||
},
|
||||
|
||||
/* Construct a [i] index for a particular node */
|
||||
_constructIndex: function(node) {
|
||||
var index = '';
|
||||
do {
|
||||
if (node.id) index = '[' + node.position + ']' + index;
|
||||
} while ((node = node.parent) != null);
|
||||
return index;
|
||||
},
|
||||
|
||||
sequence: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend(this.options(element), arguments[1] || { });
|
||||
|
||||
return $(this.findElements(element, options) || []).map( function(item) {
|
||||
return item.id.match(options.format) ? item.id.match(options.format)[1] : '';
|
||||
});
|
||||
},
|
||||
|
||||
setSequence: function(element, new_sequence) {
|
||||
element = $(element);
|
||||
var options = Object.extend(this.options(element), arguments[2] || { });
|
||||
|
||||
var nodeMap = { };
|
||||
this.findElements(element, options).each( function(n) {
|
||||
if (n.id.match(options.format))
|
||||
nodeMap[n.id.match(options.format)[1]] = [n, n.parentNode];
|
||||
n.parentNode.removeChild(n);
|
||||
});
|
||||
|
||||
new_sequence.each(function(ident) {
|
||||
var n = nodeMap[ident];
|
||||
if (n) {
|
||||
n[1].appendChild(n[0]);
|
||||
delete nodeMap[ident];
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
serialize: function(element) {
|
||||
element = $(element);
|
||||
var options = Object.extend(Sortable.options(element), arguments[1] || { });
|
||||
var name = encodeURIComponent(
|
||||
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
||||
|
||||
if (options.tree) {
|
||||
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
||||
return [name + Sortable._constructIndex(item) + "[id]=" +
|
||||
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
||||
}).flatten().join('&');
|
||||
} else {
|
||||
return Sortable.sequence(element, arguments[1]).map( function(item) {
|
||||
return name + "[]=" + encodeURIComponent(item);
|
||||
}).join('&');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if child is contained within element
|
||||
Element.isParent = function(child, element) {
|
||||
if (!child.parentNode || child == element) return false;
|
||||
if (child.parentNode == element) return true;
|
||||
return Element.isParent(child.parentNode, element);
|
||||
}
|
||||
|
||||
Element.findChildren = function(element, only, recursive, tagName) {
|
||||
if(!element.hasChildNodes()) return null;
|
||||
tagName = tagName.toUpperCase();
|
||||
if(only) only = [only].flatten();
|
||||
var elements = [];
|
||||
$A(element.childNodes).each( function(e) {
|
||||
if(e.tagName && e.tagName.toUpperCase()==tagName &&
|
||||
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
|
||||
elements.push(e);
|
||||
if(recursive) {
|
||||
var grandchildren = Element.findChildren(e, only, recursive, tagName);
|
||||
if(grandchildren) elements.push(grandchildren);
|
||||
}
|
||||
});
|
||||
|
||||
return (elements.length>0 ? elements.flatten() : []);
|
||||
}
|
||||
|
||||
Element.offsetSize = function (element, type) {
|
||||
return element['offset' + ((type=='vertical' || type=='height') ? 'Height' : 'Width')];
|
||||
}
|
1122
bbb-lti/web-app/js/prototype/effects.js
vendored
4184
bbb-lti/web-app/js/prototype/prototype.js
vendored
2691
bbb-lti/web-app/js/prototype/rico.js
vendored
58
bbb-lti/web-app/js/prototype/scriptaculous.js
vendored
@ -1,58 +0,0 @@
|
||||
// script.aculo.us scriptaculous.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining
|
||||
// a copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||
// permit persons to whom the Software is furnished to do so, subject to
|
||||
// the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
//
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
var Scriptaculous = {
|
||||
Version: '1.8.0',
|
||||
require: function(libraryName) {
|
||||
// inserting via DOM fails in Safari 2.0, so brute force approach
|
||||
document.write('<script type="text/javascript" src="'+libraryName+'"><\/script>');
|
||||
},
|
||||
REQUIRED_PROTOTYPE: '1.6.0',
|
||||
load: function() {
|
||||
function convertVersionString(versionString){
|
||||
var r = versionString.split('.');
|
||||
return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
|
||||
}
|
||||
|
||||
if((typeof Prototype=='undefined') ||
|
||||
(typeof Element == 'undefined') ||
|
||||
(typeof Element.Methods=='undefined') ||
|
||||
(convertVersionString(Prototype.Version) <
|
||||
convertVersionString(Scriptaculous.REQUIRED_PROTOTYPE)))
|
||||
throw("script.aculo.us requires the Prototype JavaScript framework >= " +
|
||||
Scriptaculous.REQUIRED_PROTOTYPE);
|
||||
|
||||
$A(document.getElementsByTagName("script")).findAll( function(s) {
|
||||
return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
|
||||
}).each( function(s) {
|
||||
var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
|
||||
var includes = s.src.match(/\?.*load=([a-z,]*)/);
|
||||
(includes ? includes[1] : 'builder,effects,dragdrop,controls,slider,sound').split(',').each(
|
||||
function(include) { Scriptaculous.require(path+include+'.js') });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Scriptaculous.load();
|
275
bbb-lti/web-app/js/prototype/slider.js
vendored
@ -1,275 +0,0 @@
|
||||
// script.aculo.us slider.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
if (!Control) var Control = { };
|
||||
|
||||
// options:
|
||||
// axis: 'vertical', or 'horizontal' (default)
|
||||
//
|
||||
// callbacks:
|
||||
// onChange(value)
|
||||
// onSlide(value)
|
||||
Control.Slider = Class.create({
|
||||
initialize: function(handle, track, options) {
|
||||
var slider = this;
|
||||
|
||||
if (Object.isArray(handle)) {
|
||||
this.handles = handle.collect( function(e) { return $(e) });
|
||||
} else {
|
||||
this.handles = [$(handle)];
|
||||
}
|
||||
|
||||
this.track = $(track);
|
||||
this.options = options || { };
|
||||
|
||||
this.axis = this.options.axis || 'horizontal';
|
||||
this.increment = this.options.increment || 1;
|
||||
this.step = parseInt(this.options.step || '1');
|
||||
this.range = this.options.range || $R(0,1);
|
||||
|
||||
this.value = 0; // assure backwards compat
|
||||
this.values = this.handles.map( function() { return 0 });
|
||||
this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
|
||||
this.options.startSpan = $(this.options.startSpan || null);
|
||||
this.options.endSpan = $(this.options.endSpan || null);
|
||||
|
||||
this.restricted = this.options.restricted || false;
|
||||
|
||||
this.maximum = this.options.maximum || this.range.end;
|
||||
this.minimum = this.options.minimum || this.range.start;
|
||||
|
||||
// Will be used to align the handle onto the track, if necessary
|
||||
this.alignX = parseInt(this.options.alignX || '0');
|
||||
this.alignY = parseInt(this.options.alignY || '0');
|
||||
|
||||
this.trackLength = this.maximumOffset() - this.minimumOffset();
|
||||
|
||||
this.handleLength = this.isVertical() ?
|
||||
(this.handles[0].offsetHeight != 0 ?
|
||||
this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
|
||||
(this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
|
||||
this.handles[0].style.width.replace(/px$/,""));
|
||||
|
||||
this.active = false;
|
||||
this.dragging = false;
|
||||
this.disabled = false;
|
||||
|
||||
if (this.options.disabled) this.setDisabled();
|
||||
|
||||
// Allowed values array
|
||||
this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
|
||||
if (this.allowedValues) {
|
||||
this.minimum = this.allowedValues.min();
|
||||
this.maximum = this.allowedValues.max();
|
||||
}
|
||||
|
||||
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
|
||||
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
||||
this.eventMouseMove = this.update.bindAsEventListener(this);
|
||||
|
||||
// Initialize handles in reverse (make sure first handle is active)
|
||||
this.handles.each( function(h,i) {
|
||||
i = slider.handles.length-1-i;
|
||||
slider.setValue(parseFloat(
|
||||
(Object.isArray(slider.options.sliderValue) ?
|
||||
slider.options.sliderValue[i] : slider.options.sliderValue) ||
|
||||
slider.range.start), i);
|
||||
h.makePositioned().observe("mousedown", slider.eventMouseDown);
|
||||
});
|
||||
|
||||
this.track.observe("mousedown", this.eventMouseDown);
|
||||
document.observe("mouseup", this.eventMouseUp);
|
||||
document.observe("mousemove", this.eventMouseMove);
|
||||
|
||||
this.initialized = true;
|
||||
},
|
||||
dispose: function() {
|
||||
var slider = this;
|
||||
Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
|
||||
Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
||||
Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
||||
this.handles.each( function(h) {
|
||||
Event.stopObserving(h, "mousedown", slider.eventMouseDown);
|
||||
});
|
||||
},
|
||||
setDisabled: function(){
|
||||
this.disabled = true;
|
||||
},
|
||||
setEnabled: function(){
|
||||
this.disabled = false;
|
||||
},
|
||||
getNearestValue: function(value){
|
||||
if (this.allowedValues){
|
||||
if (value >= this.allowedValues.max()) return(this.allowedValues.max());
|
||||
if (value <= this.allowedValues.min()) return(this.allowedValues.min());
|
||||
|
||||
var offset = Math.abs(this.allowedValues[0] - value);
|
||||
var newValue = this.allowedValues[0];
|
||||
this.allowedValues.each( function(v) {
|
||||
var currentOffset = Math.abs(v - value);
|
||||
if (currentOffset <= offset){
|
||||
newValue = v;
|
||||
offset = currentOffset;
|
||||
}
|
||||
});
|
||||
return newValue;
|
||||
}
|
||||
if (value > this.range.end) return this.range.end;
|
||||
if (value < this.range.start) return this.range.start;
|
||||
return value;
|
||||
},
|
||||
setValue: function(sliderValue, handleIdx){
|
||||
if (!this.active) {
|
||||
this.activeHandleIdx = handleIdx || 0;
|
||||
this.activeHandle = this.handles[this.activeHandleIdx];
|
||||
this.updateStyles();
|
||||
}
|
||||
handleIdx = handleIdx || this.activeHandleIdx || 0;
|
||||
if (this.initialized && this.restricted) {
|
||||
if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
|
||||
sliderValue = this.values[handleIdx-1];
|
||||
if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
|
||||
sliderValue = this.values[handleIdx+1];
|
||||
}
|
||||
sliderValue = this.getNearestValue(sliderValue);
|
||||
this.values[handleIdx] = sliderValue;
|
||||
this.value = this.values[0]; // assure backwards compat
|
||||
|
||||
this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
|
||||
this.translateToPx(sliderValue);
|
||||
|
||||
this.drawSpans();
|
||||
if (!this.dragging || !this.event) this.updateFinished();
|
||||
},
|
||||
setValueBy: function(delta, handleIdx) {
|
||||
this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
|
||||
handleIdx || this.activeHandleIdx || 0);
|
||||
},
|
||||
translateToPx: function(value) {
|
||||
return Math.round(
|
||||
((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
|
||||
(value - this.range.start)) + "px";
|
||||
},
|
||||
translateToValue: function(offset) {
|
||||
return ((offset/(this.trackLength-this.handleLength) *
|
||||
(this.range.end-this.range.start)) + this.range.start);
|
||||
},
|
||||
getRange: function(range) {
|
||||
var v = this.values.sortBy(Prototype.K);
|
||||
range = range || 0;
|
||||
return $R(v[range],v[range+1]);
|
||||
},
|
||||
minimumOffset: function(){
|
||||
return(this.isVertical() ? this.alignY : this.alignX);
|
||||
},
|
||||
maximumOffset: function(){
|
||||
return(this.isVertical() ?
|
||||
(this.track.offsetHeight != 0 ? this.track.offsetHeight :
|
||||
this.track.style.height.replace(/px$/,"")) - this.alignY :
|
||||
(this.track.offsetWidth != 0 ? this.track.offsetWidth :
|
||||
this.track.style.width.replace(/px$/,"")) - this.alignX);
|
||||
},
|
||||
isVertical: function(){
|
||||
return (this.axis == 'vertical');
|
||||
},
|
||||
drawSpans: function() {
|
||||
var slider = this;
|
||||
if (this.spans)
|
||||
$R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
|
||||
if (this.options.startSpan)
|
||||
this.setSpan(this.options.startSpan,
|
||||
$R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
|
||||
if (this.options.endSpan)
|
||||
this.setSpan(this.options.endSpan,
|
||||
$R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
|
||||
},
|
||||
setSpan: function(span, range) {
|
||||
if (this.isVertical()) {
|
||||
span.style.top = this.translateToPx(range.start);
|
||||
span.style.height = this.translateToPx(range.end - range.start + this.range.start);
|
||||
} else {
|
||||
span.style.left = this.translateToPx(range.start);
|
||||
span.style.width = this.translateToPx(range.end - range.start + this.range.start);
|
||||
}
|
||||
},
|
||||
updateStyles: function() {
|
||||
this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
|
||||
Element.addClassName(this.activeHandle, 'selected');
|
||||
},
|
||||
startDrag: function(event) {
|
||||
if (Event.isLeftClick(event)) {
|
||||
if (!this.disabled){
|
||||
this.active = true;
|
||||
|
||||
var handle = Event.element(event);
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
var track = handle;
|
||||
if (track==this.track) {
|
||||
var offsets = Position.cumulativeOffset(this.track);
|
||||
this.event = event;
|
||||
this.setValue(this.translateToValue(
|
||||
(this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
|
||||
));
|
||||
var offsets = Position.cumulativeOffset(this.activeHandle);
|
||||
this.offsetX = (pointer[0] - offsets[0]);
|
||||
this.offsetY = (pointer[1] - offsets[1]);
|
||||
} else {
|
||||
// find the handle (prevents issues with Safari)
|
||||
while((this.handles.indexOf(handle) == -1) && handle.parentNode)
|
||||
handle = handle.parentNode;
|
||||
|
||||
if (this.handles.indexOf(handle)!=-1) {
|
||||
this.activeHandle = handle;
|
||||
this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
|
||||
this.updateStyles();
|
||||
|
||||
var offsets = Position.cumulativeOffset(this.activeHandle);
|
||||
this.offsetX = (pointer[0] - offsets[0]);
|
||||
this.offsetY = (pointer[1] - offsets[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Event.stop(event);
|
||||
}
|
||||
},
|
||||
update: function(event) {
|
||||
if (this.active) {
|
||||
if (!this.dragging) this.dragging = true;
|
||||
this.draw(event);
|
||||
if (Prototype.Browser.WebKit) window.scrollBy(0,0);
|
||||
Event.stop(event);
|
||||
}
|
||||
},
|
||||
draw: function(event) {
|
||||
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
||||
var offsets = Position.cumulativeOffset(this.track);
|
||||
pointer[0] -= this.offsetX + offsets[0];
|
||||
pointer[1] -= this.offsetY + offsets[1];
|
||||
this.event = event;
|
||||
this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
|
||||
if (this.initialized && this.options.onSlide)
|
||||
this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
|
||||
},
|
||||
endDrag: function(event) {
|
||||
if (this.active && this.dragging) {
|
||||
this.finishDrag(event, true);
|
||||
Event.stop(event);
|
||||
}
|
||||
this.active = false;
|
||||
this.dragging = false;
|
||||
},
|
||||
finishDrag: function(event, success) {
|
||||
this.active = false;
|
||||
this.dragging = false;
|
||||
this.updateFinished();
|
||||
},
|
||||
updateFinished: function() {
|
||||
if (this.initialized && this.options.onChange)
|
||||
this.options.onChange(this.values.length>1 ? this.values : this.value, this);
|
||||
this.event = null;
|
||||
}
|
||||
});
|
55
bbb-lti/web-app/js/prototype/sound.js
vendored
@ -1,55 +0,0 @@
|
||||
// script.aculo.us sound.js v1.8.0, Tue Nov 06 15:01:40 +0300 2007
|
||||
|
||||
// Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
||||
//
|
||||
// Based on code created by Jules Gravinese (http://www.webveteran.com/)
|
||||
//
|
||||
// script.aculo.us is freely distributable under the terms of an MIT-style license.
|
||||
// For details, see the script.aculo.us web site: http://script.aculo.us/
|
||||
|
||||
Sound = {
|
||||
tracks: {},
|
||||
_enabled: true,
|
||||
template:
|
||||
new Template('<embed style="height:0" id="sound_#{track}_#{id}" src="#{url}" loop="false" autostart="true" hidden="true"/>'),
|
||||
enable: function(){
|
||||
Sound._enabled = true;
|
||||
},
|
||||
disable: function(){
|
||||
Sound._enabled = false;
|
||||
},
|
||||
play: function(url){
|
||||
if(!Sound._enabled) return;
|
||||
var options = Object.extend({
|
||||
track: 'global', url: url, replace: false
|
||||
}, arguments[1] || {});
|
||||
|
||||
if(options.replace && this.tracks[options.track]) {
|
||||
$R(0, this.tracks[options.track].id).each(function(id){
|
||||
var sound = $('sound_'+options.track+'_'+id);
|
||||
sound.Stop && sound.Stop();
|
||||
sound.remove();
|
||||
})
|
||||
this.tracks[options.track] = null;
|
||||
}
|
||||
|
||||
if(!this.tracks[options.track])
|
||||
this.tracks[options.track] = { id: 0 }
|
||||
else
|
||||
this.tracks[options.track].id++;
|
||||
|
||||
options.id = this.tracks[options.track].id;
|
||||
$$('body')[0].insert(
|
||||
Prototype.Browser.IE ? new Element('bgsound',{
|
||||
id: 'sound_'+options.track+'_'+options.id,
|
||||
src: options.url, loop: 1, autostart: true
|
||||
}) : Sound.template.evaluate(options));
|
||||
}
|
||||
};
|
||||
|
||||
if(Prototype.Browser.Gecko && navigator.userAgent.indexOf("Win") > 0){
|
||||
if(navigator.plugins && $A(navigator.plugins).detect(function(p){ return p.name.indexOf('QuickTime') != -1 }))
|
||||
Sound.template = new Template('<object id="sound_#{track}_#{id}" width="0" height="0" type="audio/mpeg" data="#{url}"/>')
|
||||
else
|
||||
Sound.play = function(){}
|
||||
}
|