Merge remote-tracking branch 'upstream/master' into settings-accessibility

This commit is contained in:
Matthew Marangoni 2016-08-24 07:37:54 -07:00
commit f0a5c550a7
390 changed files with 11662 additions and 26077 deletions

View File

@ -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)

View File

@ -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);

View File

@ -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) {

View File

@ -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 }
}
}

View File

@ -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) {

View File

@ -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())
}

View File

@ -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))
}
}
}

View File

@ -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))
}
}
}

View File

@ -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,

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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)

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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);

View File

@ -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())
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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>

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

Before

Width:  |  Height:  |  Size: 9.8 KiB

After

Width:  |  Height:  |  Size: 9.8 KiB

View File

Before

Width:  |  Height:  |  Size: 6.6 KiB

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

Before

Width:  |  Height:  |  Size: 65 B

After

Width:  |  Height:  |  Size: 65 B

View File

Before

Width:  |  Height:  |  Size: 400 B

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 80 B

After

Width:  |  Height:  |  Size: 80 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 67 B

After

Width:  |  Height:  |  Size: 67 B

View File

Before

Width:  |  Height:  |  Size: 658 B

After

Width:  |  Height:  |  Size: 658 B

View File

Before

Width:  |  Height:  |  Size: 659 B

After

Width:  |  Height:  |  Size: 659 B

View File

Before

Width:  |  Height:  |  Size: 767 B

After

Width:  |  Height:  |  Size: 767 B

View File

Before

Width:  |  Height:  |  Size: 755 B

After

Width:  |  Height:  |  Size: 755 B

View File

Before

Width:  |  Height:  |  Size: 726 B

After

Width:  |  Height:  |  Size: 726 B

View File

Before

Width:  |  Height:  |  Size: 701 B

After

Width:  |  Height:  |  Size: 701 B

View File

Before

Width:  |  Height:  |  Size: 806 B

After

Width:  |  Height:  |  Size: 806 B

View File

Before

Width:  |  Height:  |  Size: 778 B

After

Width:  |  Height:  |  Size: 778 B

View File

Before

Width:  |  Height:  |  Size: 300 B

After

Width:  |  Height:  |  Size: 300 B

View File

Before

Width:  |  Height:  |  Size: 835 B

After

Width:  |  Height:  |  Size: 835 B

View File

Before

Width:  |  Height:  |  Size: 834 B

After

Width:  |  Height:  |  Size: 834 B

View File

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View 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
*/

View File

@ -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;

View File

@ -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'
}
}

View File

@ -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 ->

View File

@ -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'
}
}

View File

@ -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'
}

View File

@ -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')
}
}
}

View File

@ -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"?>' +

View File

@ -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;
}

View File

@ -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">

View File

@ -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&hellip;"/></div>
<r:layoutResources />
</body>
</html>

View File

@ -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">

View File

@ -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
View 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
View 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

View File

@ -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>

View File

@ -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>

View File

@ -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;

View File

@ -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>

View File

@ -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>

File diff suppressed because it is too large Load Diff

View 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -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(/"/,'&quot;') + '"');
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)));
}
});
}
}

View File

@ -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));
}
});

View File

@ -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')];
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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();

View File

@ -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;
}
});

View File

@ -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(){}
}

Some files were not shown because too many files have changed in this diff Show More