Merge branch 'testing-version-002' of https://github.com/antobinary/bigbluebutton into meteor-client-whiteboard
This commit is contained in:
commit
e3f98d488d
@ -110,16 +110,20 @@ class BigBlueButtonActor(outGW: MessageOutGateway) extends Actor {
|
||||
|
||||
for(i <- 0 until arr.length) {
|
||||
val id = arr(i)
|
||||
val duration = meetings.get(arr(i)).head.getDuration()
|
||||
val name = meetings.get(arr(i)).head.getMeetingName()
|
||||
val recorded = meetings.get(arr(i)).head.getRecordedStatus()
|
||||
val voiceBridge = meetings.get(arr(i)).head.getVoiceBridgeNumber()
|
||||
|
||||
var info = new MeetingInfo(id, name, recorded)
|
||||
var info = new MeetingInfo(id, name, recorded, voiceBridge, duration)
|
||||
resultArray(i) = info
|
||||
|
||||
//remove later
|
||||
println("for a meeting:" + id)
|
||||
println("Meeting Name = " + meetings.get(id).head.getMeetingName())
|
||||
println("isRecorded = " + meetings.get(id).head.getRecordedStatus())
|
||||
println("voiceBridge = " + voiceBridge)
|
||||
println("duration = " + duration)
|
||||
|
||||
//send the users
|
||||
this ! (new GetUsers(id, "nodeJSapp"))
|
||||
|
@ -29,6 +29,10 @@ class MeetingActor(val meetingID: String, val meetingName: String, val recorded:
|
||||
var muted = false;
|
||||
var meetingEnded = false
|
||||
|
||||
def getDuration():Long = {
|
||||
duration
|
||||
}
|
||||
|
||||
def getMeetingName():String = {
|
||||
meetingName
|
||||
}
|
||||
@ -36,6 +40,10 @@ class MeetingActor(val meetingID: String, val meetingName: String, val recorded:
|
||||
def getRecordedStatus():Boolean = {
|
||||
recorded
|
||||
}
|
||||
|
||||
def getVoiceBridgeNumber():String = {
|
||||
voiceBridge
|
||||
}
|
||||
|
||||
val TIMER_INTERVAL = 30000
|
||||
var hasLastWebUserLeft = false
|
||||
|
@ -104,4 +104,4 @@ case class MeetingPasswords(moderatorPass: String, viewerPass: String)
|
||||
case class MeetingDuration(duration: Int = 0, createdTime: Long = 0,
|
||||
startTime: Long = 0, endTime: Long = 0)
|
||||
|
||||
case class MeetingInfo(meetingID: String, meetingName: String, recorded: Boolean)
|
||||
case class MeetingInfo(meetingID: String, meetingName: String, recorded: Boolean, voiceBridge: String, duration: Long)
|
||||
|
@ -13,9 +13,16 @@ login = (req, resp) ->
|
||||
serverAndSecret = testapi.serverAndSecret
|
||||
|
||||
#use the name from the textbox
|
||||
console.log "\n\nThe Username passed was=" + JSON.stringify(req.body.name) + "\n\n"
|
||||
joinParams.fullName = JSON.stringify req.body.name
|
||||
joinParams.fullName = joinParams.fullName.replace(/['"]/g,'')
|
||||
console.log "\n\nThe Username passed was=" + JSON.stringify(req.body.name) + "The Meetingname passed was=" + JSON.stringify(req.body.meetingName) + "\n\n"
|
||||
|
||||
# grab the username and the meeting name passed in. Strip the surrounding quotes
|
||||
joinParams.fullName = (JSON.stringify req.body.name)?.replace(/['"]/g,'')
|
||||
passedMeetingName = (JSON.stringify req.body.meetingName)?.replace(/["]/g,'')
|
||||
|
||||
# use the meeting name from the form to [create if not existing and] join the meeting with such name
|
||||
joinParams.meetingID = passedMeetingName
|
||||
createParams.name = passedMeetingName
|
||||
createParams.meetingID = passedMeetingName
|
||||
|
||||
#calling createapi
|
||||
bbbapi.create(createParams, serverAndSecret, {}, (errorOuter, responseOuter, bodyOuter) ->
|
||||
|
@ -5,7 +5,8 @@ myModule.controller('MainCtrl', function($scope, $http, $location, $window) {
|
||||
$scope.postUsername = function() {
|
||||
var account = {
|
||||
"name": $scope.username,
|
||||
"password": 'oOoOoO'
|
||||
"password": 'oOoOoO',
|
||||
"meetingName": $scope.meetingName
|
||||
};
|
||||
jQuery.getJSON("config.json", function (json) {
|
||||
$http.post('/login', account).success(function(res) {
|
||||
|
@ -9,6 +9,9 @@
|
||||
<div>
|
||||
<label>Username:</label>
|
||||
<input type="text" ng-model="username" placeholder="Enter a name here">
|
||||
<label>Meeting name:</label>
|
||||
<input type="text" ng-model="meetingName" placeholder="Enter a meeting name">
|
||||
<label>(e.x. Demo Meeting)</label>
|
||||
</div>
|
||||
<div ng-controller="MainCtrl">
|
||||
<form ng-submit="postUsername()">
|
||||
|
@ -20,6 +20,9 @@
|
||||
meet?.meetingName
|
||||
else null
|
||||
|
||||
@getTimeOfJoining = ->
|
||||
Meteor.Users.findOne({"user.userid": getInSession("userId")})?.user?.time_of_joining
|
||||
|
||||
# Finds the names of all people the current user is in a private conversation with
|
||||
# Removes yourself and duplicates if they exist
|
||||
@getPrivateChatees = ->
|
||||
@ -123,6 +126,12 @@ Handlebars.registerHelper "isUserSharingAudio", (u) ->
|
||||
user?.user?.voiceUser?.joined
|
||||
else return false
|
||||
|
||||
Handlebars.registerHelper "isUserListenOnly", (u) ->
|
||||
if u?
|
||||
user = Meteor.Users.findOne({userId:u.userid})
|
||||
user?.user?.listenOnly
|
||||
else return false
|
||||
|
||||
Handlebars.registerHelper "isUserSharingVideo", (u) ->
|
||||
u.webcam_stream?.length isnt 0
|
||||
|
||||
@ -221,7 +230,7 @@ Meteor.methods
|
||||
username = "#{getInSession("userId")}-bbbID-#{getUsersName()}"
|
||||
# voicePin = Meteor.Meetings.findOne()?.voiceConf
|
||||
# voiceBridge = if voicePin? then voicePin else "0"
|
||||
voiceBridge = "70827"
|
||||
voiceBridge = Meteor.Meetings.findOne({}).voiceConf # need to know this info for all meetings #TODO
|
||||
server = null
|
||||
joinCallback = (message) ->
|
||||
console.log JSON.stringify message
|
||||
@ -275,4 +284,5 @@ Meteor.methods
|
||||
@getCurrentSlideDoc = -> # returns only one document
|
||||
currentPresentation = Meteor.Presentations.findOne({"presentation.current": true})
|
||||
presentationId = currentPresentation?.presentation?.id
|
||||
currentSlide = Meteor.Slides.findOne({"presentationId": presentationId, "slide.current": true})
|
||||
currentSlide = Meteor.Slides.findOne({"presentationId": presentationId, "slide.current": true})
|
||||
|
||||
|
@ -35,6 +35,12 @@ Template.header.events
|
||||
Meteor.call('userLowerHand', getInSession("meetingId"), getInSession("userId"), loweredBy)
|
||||
"click .whiteboardIcon": (event) ->
|
||||
toggleWhiteBoard()
|
||||
"mouseover #navbarMinimizedButton": (event) ->
|
||||
$("#navbarMinimizedButton").removeClass("navbarMinimizedButtonSmall")
|
||||
$("#navbarMinimizedButton").addClass("navbarMinimizedButtonLarge")
|
||||
"mouseout #navbarMinimizedButton": (event) ->
|
||||
$("#navbarMinimizedButton").removeClass("navbarMinimizedButtonLarge")
|
||||
$("#navbarMinimizedButton").addClass("navbarMinimizedButtonSmall")
|
||||
|
||||
Template.recordingStatus.rendered = ->
|
||||
$('button[rel=tooltip]').tooltip()
|
||||
@ -46,10 +52,6 @@ Template.main.helpers
|
||||
Template.makeButton.rendered = ->
|
||||
$('button[rel=tooltip]').tooltip()
|
||||
|
||||
# Gets called last in main template, just an easy place to print stuff out
|
||||
Handlebars.registerHelper "doFinalStuff", ->
|
||||
console.log "-----Doing Final Stuff-----"
|
||||
|
||||
# These settings can just be stored locally in session, created at start up
|
||||
Meteor.startup ->
|
||||
@SessionAmplify = _.extend({}, Session,
|
||||
|
@ -69,11 +69,9 @@
|
||||
{{> makeButton btn_class="hideNavbarIcon navbarButton" i_class="chevron-up" rel="tooltip" data_placement="bottom" title="Hide Navbar"}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="navbarFiller"></div>
|
||||
{{else}}
|
||||
<div id="navbar" class="myNavbarMinimized navbar-default navbar-static-top" role="navigation">
|
||||
<!-- User wants to hide navbar. The button for bringing the navbar back needs to still be available. Perhaps it should appear/disappear in the future on hover? Something to add later. -->
|
||||
{{> makeButton btn_class="hideNavbarIcon navbarMinimizedButton" i_class="chevron-down" rel="tooltip" data_placement="bottom" title="Display Navbar"}}
|
||||
</div>
|
||||
{{> makeButton id="navbarMinimizedButton" btn_class="hideNavbarIcon navbarMinimizedButtonSmall" i_class="chevron-down" rel="tooltip" data_placement="bottom" title="Display Navbar"}}
|
||||
{{/if}}
|
||||
</template>
|
||||
|
||||
@ -87,7 +85,6 @@
|
||||
{{> chatbar id="chat" title="Chat" name="chatbar"}}
|
||||
<audio id="remote-media" autoplay="autoplay"></audio>
|
||||
{{> footer}}
|
||||
{{doFinalStuff}}
|
||||
</div>
|
||||
</body>
|
||||
</template>
|
||||
|
@ -1,3 +1,8 @@
|
||||
bottomEntry{
|
||||
border: none;
|
||||
padding-bottom: 0px;
|
||||
padding-top: 0px;
|
||||
}
|
||||
.chat{
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
|
@ -19,6 +19,7 @@ body {
|
||||
border: 1px solid #ccc;
|
||||
float: left;
|
||||
height:90%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.gradientBar{
|
||||
background: -webkit-linear-gradient(rgb(255,255,255), rgb(182,181,181)); /* For Safari 5.1 to 6.0 */
|
||||
@ -30,7 +31,6 @@ body {
|
||||
.lowerHand{}
|
||||
.mainContainer{
|
||||
height:100%;
|
||||
padding-top:60px;
|
||||
}
|
||||
.myFooter{
|
||||
color:black;
|
||||
@ -58,16 +58,29 @@ body {
|
||||
margin-right: 2px;
|
||||
width:40px;
|
||||
}
|
||||
.navbarFiller{
|
||||
height:50px;
|
||||
width:100%;
|
||||
}
|
||||
.navbarIconToggleActive{
|
||||
border-bottom: 4px solid lightblue;
|
||||
}
|
||||
.navbarMinimizedButton{
|
||||
height:20px;
|
||||
margin-bottom:0px;
|
||||
margin-left: 2px;
|
||||
margin-right: 20px;
|
||||
margin-top:0px;
|
||||
width:40px;
|
||||
#navbarMinimizedButton{
|
||||
margin-bottom:0px;
|
||||
margin-left: 2px;
|
||||
margin-right: 20px;
|
||||
margin-top:0px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top:0;
|
||||
}
|
||||
.navbarMinimizedButtonSmall{
|
||||
height:10px;
|
||||
width:40px;
|
||||
}
|
||||
.navbarMinimizedButtonLarge{
|
||||
height:50px;
|
||||
width:40px;
|
||||
}
|
||||
.navbarSection{
|
||||
float:left;
|
||||
|
@ -13,7 +13,7 @@
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 90px;
|
||||
width: 100px;
|
||||
}
|
||||
.user-entry tr:nth-child(even) {background-color: rgb(245,245,245);}
|
||||
.user-entry tr:hover {font-weight: bold;}
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
chattingWith = getInSession('inChatWith')
|
||||
|
||||
if chattingWith isnt "PUBLIC_CHAT"
|
||||
if chattingWith isnt "PUBLIC_CHAT"
|
||||
dest = Meteor.Users.findOne("userId": chattingWith)
|
||||
|
||||
messageForServer = { # construct message for server
|
||||
@ -28,97 +28,124 @@
|
||||
Template.chatInput.events
|
||||
'click #sendMessageButton': (event) ->
|
||||
sendMessage()
|
||||
|
||||
'keypress #newMessageInput': (event) -> # user pressed a button inside the chatbox
|
||||
if event.shiftKey and event.which is 13
|
||||
$("#newMessageInput").append("\n")
|
||||
return
|
||||
|
||||
if event.which is 13 # Check for pressing enter to submit message
|
||||
sendMessage()
|
||||
$('#newMessageInput').val("")
|
||||
return false
|
||||
|
||||
Template.chatInput.rendered = ->
|
||||
$('input[rel=tooltip]').tooltip()
|
||||
$('button[rel=tooltip]').tooltip()
|
||||
|
||||
Template.chatbar.helpers
|
||||
getChatGreeting: ->
|
||||
greeting = "Welcome to #{getMeetingName()}!\n\n
|
||||
For help on using BigBlueButton see these (short) <a href='http://www.bigbluebutton.org/videos/' target='_blank'>tutorial videos</a>.\n\n
|
||||
To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.\n\n\n
|
||||
This server is running BigBlueButton #{getInSession 'bbbServerVersion'}."
|
||||
getChatGreeting: ->
|
||||
greeting = "Welcome to #{getMeetingName()}!\n\n
|
||||
For help on using BigBlueButton see these (short) <a href='http://www.bigbluebutton.org/videos/' target='_blank'>tutorial videos</a>.\n\n
|
||||
To join the audio bridge click the headset icon (upper-left hand corner). Use a headset to avoid causing background noise for others.\n\n\n
|
||||
This server is running BigBlueButton #{getInSession 'bbbServerVersion'}."
|
||||
|
||||
# This method returns all messages for the user. It looks at the session to determine whether the user is in
|
||||
#private or public chat. If true is passed, messages returned are from before the user joined. Else, the messages are from after the user joined
|
||||
getFormattedMessagesForChat: () ->
|
||||
friend = chattingWith = getInSession('inChatWith') # the recipient(s) of the messages
|
||||
after = before = greeting = []
|
||||
# This method returns all messages for the user. It looks at the session to determine whether the user is in
|
||||
#private or public chat. If true is passed, messages returned are from before the user joined. Else, the messages are from after the user joined
|
||||
getFormattedMessagesForChat: () ->
|
||||
friend = chattingWith = getInSession('inChatWith') # the recipient(s) of the messages
|
||||
after = before = greeting = []
|
||||
|
||||
if chattingWith is 'PUBLIC_CHAT' # find all public messages
|
||||
before = Meteor.Chat.find({'message.chat_type': chattingWith, 'message.from_time': {$lt: String(getInSession("joinedAt"))}}).fetch()
|
||||
after = Meteor.Chat.find({'message.chat_type': chattingWith, 'message.from_time': {$gt: String(getInSession("joinedAt"))}}).fetch()
|
||||
if chattingWith is 'PUBLIC_CHAT' # find all public messages
|
||||
before = Meteor.Chat.find({'message.chat_type': chattingWith, 'message.from_time': {$lt: String(getTimeOfJoining())}}).fetch()
|
||||
after = Meteor.Chat.find({'message.chat_type': chattingWith, 'message.from_time': {$gt: String(getTimeOfJoining())}}).fetch()
|
||||
|
||||
greeting = [
|
||||
'message':
|
||||
'message': Template.chatbar.getChatGreeting(),
|
||||
'from_username': 'System',
|
||||
'from_time': getTime()
|
||||
'from_color': '0x3399FF' # A nice blue in hex
|
||||
]
|
||||
else
|
||||
me = getInSession("userId")
|
||||
after = Meteor.Chat.find({ # find all messages between current user and recipient
|
||||
'message.chat_type': 'PRIVATE_CHAT',
|
||||
$or: [{'message.from_userid': me, 'message.to_userid': friend},{'message.from_userid': friend, 'message.to_userid': me}]
|
||||
}).fetch()
|
||||
greeting = [
|
||||
'message':
|
||||
'message': Template.chatbar.getChatGreeting(),
|
||||
'from_username': 'System',
|
||||
'from_time': getTimeOfJoining()
|
||||
'from_color': '0x3399FF' # A nice blue in hex
|
||||
]
|
||||
else
|
||||
me = getInSession("userId")
|
||||
after = Meteor.Chat.find({ # find all messages between current user and recipient
|
||||
'message.chat_type': 'PRIVATE_CHAT',
|
||||
$or: [{'message.from_userid': me, 'message.to_userid': friend},{'message.from_userid': friend, 'message.to_userid': me}]
|
||||
}).fetch()
|
||||
|
||||
messages = (before.concat greeting).concat after
|
||||
messages = (before.concat greeting).concat after
|
||||
|
||||
getCombinedMessagesForChat: ->
|
||||
msgs = Template.chatbar.getFormattedMessagesForChat()
|
||||
prev_time = msgs[0]?.message.from_time
|
||||
prev_userid = msgs[0]?.message.from_userid
|
||||
for i in [0...msgs.length]
|
||||
if i != 0
|
||||
if prev_userid is msgs[i].message.from_userid
|
||||
msgs[i].message.from_username = ''
|
||||
if Template.message.toClockTime(msgs[i].message.from_time) is Template.message.toClockTime(prev_time)
|
||||
prev_time = msgs[i].message.from_time
|
||||
msgs[i].message.from_time = null
|
||||
else
|
||||
prev_time = msgs[i].message.from_time
|
||||
else
|
||||
prev_time = msgs[i].message.from_time
|
||||
prev_userid = msgs[i].message.from_userid
|
||||
msgs
|
||||
|
||||
Template.message.rendered = -> # When a message has been added and finished rendering, scroll to the bottom of the chat
|
||||
$('#chatbody').scrollTop($('#chatbody')[0].scrollHeight)
|
||||
getCombinedMessagesForChat: ->
|
||||
msgs = Template.chatbar.getFormattedMessagesForChat()
|
||||
len = msgs.length # get length of messages
|
||||
i = 0
|
||||
while i < len # Must be a do while, for loop compiles and stores the length of array which can change inside the loop!
|
||||
if msgs[i].message.from_userid isnt 'System' # skip system messages
|
||||
j = i+1 # Start looking at messages right after the current one
|
||||
|
||||
while j < len
|
||||
deleted = false
|
||||
if msgs[j].message.from_userid isnt 'System' # Ignore system messages
|
||||
# Check if the time discrepancy between the two messages exceeds window for grouping
|
||||
if (parseFloat(msgs[j].message.from_time)-parseFloat(msgs[i].message.from_time)) >= 60000 # 60 seconds/1 minute
|
||||
break # Messages are too far between, so them seperated and stop joining here
|
||||
|
||||
if msgs[i].message.from_userid is msgs[j].message.from_userid # Both messages are from the same user
|
||||
msgs[i].message.message += "\\n#{msgs[j].message.message}" # Combine the messages
|
||||
msgs.splice(j,1) # Delete the message from the collection
|
||||
deleted=true
|
||||
else break # Messages are from different people, move on
|
||||
#
|
||||
else break # This is the break point in the chat, don't merge
|
||||
#
|
||||
len = msgs.length
|
||||
++j if not deleted
|
||||
#
|
||||
++i
|
||||
len = msgs.length
|
||||
|
||||
msgs
|
||||
|
||||
|
||||
# When chatbar gets rendered, scroll to the bottom
|
||||
Template.chatbar.rendered = ->
|
||||
$('#chatbody').scrollTop($('#chatbody')[0]?.scrollHeight)
|
||||
false
|
||||
# Scrolls the message container to the bottom. The number of pixels to scroll down is the height of the container
|
||||
Handlebars.registerHelper "autoscroll", ->
|
||||
$('#chatbody').scrollTop($('#chatbody')[0]?.scrollHeight)
|
||||
false
|
||||
|
||||
Template.optionsBar.events
|
||||
'click .private-chat-user-entry': (event) -> # clicked a user's name to begin private chat
|
||||
setInSession 'display_chatPane', true
|
||||
setInSession "inChatWith", @userId
|
||||
me = getInSession("userId")
|
||||
'click .private-chat-user-entry': (event) -> # clicked a user's name to begin private chat
|
||||
setInSession 'display_chatPane', true
|
||||
setInSession "inChatWith", @userId
|
||||
me = getInSession("userId")
|
||||
|
||||
if Meteor.Chat.find({'message.chat_type': 'PRIVATE_CHAT', $or: [{'message.from_userid': me, 'message.to_userid': @userId},{'message.from_userid': @userId, 'message.to_userid': me}]}).fetch().length is 0
|
||||
messageForServer =
|
||||
"message": "#{getUsersName()} has joined private chat with #{@user.name}."
|
||||
"chat_type": "PRIVATE_CHAT"
|
||||
"from_userid": me
|
||||
"from_username": getUsersName()
|
||||
"from_tz_offset": "240"
|
||||
"to_username": @user.name
|
||||
"to_userid": @userId
|
||||
"from_lang": "en"
|
||||
"from_time": getTime()
|
||||
"from_color": "0"
|
||||
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer
|
||||
if Meteor.Chat.find({'message.chat_type': 'PRIVATE_CHAT', $or: [{'message.from_userid': me, 'message.to_userid': @userId},{'message.from_userid': @userId, 'message.to_userid': me}]}).fetch().length is 0
|
||||
messageForServer =
|
||||
"message": "#{getUsersName()} has joined private chat with #{@user.name}."
|
||||
"chat_type": "PRIVATE_CHAT"
|
||||
"from_userid": me
|
||||
"from_username": getUsersName()
|
||||
"from_tz_offset": "240"
|
||||
"to_username": @user.name
|
||||
"to_userid": @userId
|
||||
"from_lang": "en"
|
||||
"from_time": getTime()
|
||||
"from_color": "0"
|
||||
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer
|
||||
|
||||
Template.optionsBar.rendered = ->
|
||||
$('div[rel=tooltip]').tooltip()
|
||||
$('div[rel=tooltip]').tooltip()
|
||||
|
||||
Template.optionsFontSize.events
|
||||
"click .fontSizeSelector": (event) ->
|
||||
selectedFontSize = parseInt(event.target.id)
|
||||
if selectedFontSize
|
||||
setInSession "messageFontSize", selectedFontSize
|
||||
else setInSession "messageFontSize", 12
|
||||
"click .fontSizeSelector": (event) ->
|
||||
selectedFontSize = parseInt(event.target.id)
|
||||
if selectedFontSize
|
||||
setInSession "messageFontSize", selectedFontSize
|
||||
else setInSession "messageFontSize", 12
|
||||
|
||||
Template.tabButtons.events
|
||||
'click .close': (event) -> # user closes private chat
|
||||
@ -157,31 +184,31 @@ Template.tabButtons.helpers
|
||||
button
|
||||
|
||||
Template.message.helpers
|
||||
activateBreakLines: (str) ->
|
||||
res = str.replace /\n/gim, '<br/>'
|
||||
res = res.replace /\r/gim, '<br/>'
|
||||
|
||||
# make links received from Flash client clickable in HTML
|
||||
toClickable: (str) ->
|
||||
res = str.replace /<a href='event:/gim, "<a target='_blank' href='"
|
||||
res = res.replace /<a href="event:/gim, '<a target="_blank" href="'
|
||||
activateBreakLines: (str) ->
|
||||
res = str.replace /\\n/gim, '<br/>'
|
||||
res = res.replace /\r/gim, '<br/>'
|
||||
|
||||
# make links received from Flash client clickable in HTML
|
||||
toClickable: (str) ->
|
||||
res = str.replace /<a href='event:/gim, "<a target='_blank' href='"
|
||||
res = res.replace /<a href="event:/gim, '<a target="_blank" href="'
|
||||
|
||||
toClockTime: (epochTime) ->
|
||||
if epochTime is null
|
||||
return ""
|
||||
local = new Date()
|
||||
offset = local.getTimezoneOffset()
|
||||
epochTime = epochTime - offset * 60000 # 1 min = 60 s = 60,000 ms
|
||||
dateObj = new Date(epochTime)
|
||||
hours = dateObj.getUTCHours()
|
||||
minutes = dateObj.getUTCMinutes()
|
||||
if minutes < 10
|
||||
minutes = "0" + minutes
|
||||
hours + ":" + minutes
|
||||
toClockTime: (epochTime) ->
|
||||
if epochTime is null
|
||||
return ""
|
||||
local = new Date()
|
||||
offset = local.getTimezoneOffset()
|
||||
epochTime = epochTime - offset * 60000 # 1 min = 60 s = 60,000 ms
|
||||
dateObj = new Date(epochTime)
|
||||
hours = dateObj.getUTCHours()
|
||||
minutes = dateObj.getUTCMinutes()
|
||||
if minutes < 10
|
||||
minutes = "0" + minutes
|
||||
hours + ":" + minutes
|
||||
|
||||
sanitizeAndFormat: (str) ->
|
||||
# First, replace replace all tags with the ascii equivalent (excluding those involved in anchor tags)
|
||||
res = str.replace(/&/g, '&').replace(/<(?![au\/])/g, '<').replace(/\/([^au])>/g, '$1>').replace(/([^=])"(?!>)/g, '$1"');
|
||||
|
||||
res = Template.message.toClickable res
|
||||
res = Template.message.activateBreakLines res
|
||||
sanitizeAndFormat: (str) ->
|
||||
# First, replace replace all tags with the ascii equivalent (excluding those involved in anchor tags)
|
||||
res = str.replace(/&/g, '&').replace(/<(?![au\/])/g, '<').replace(/\/([^au])>/g, '$1>').replace(/([^=])"(?!>)/g, '$1"');
|
||||
|
||||
res = Template.message.toClickable res
|
||||
res = Template.message.activateBreakLines res
|
||||
|
@ -7,8 +7,11 @@
|
||||
<div id="chatbody">
|
||||
<ul class="chat">
|
||||
{{#each getCombinedMessagesForChat}}
|
||||
<li {{messageFontSize}}>{{> message}}</li>
|
||||
{{#if message}}
|
||||
<li {{messageFontSize}}>{{> message}}</li>
|
||||
{{/if}}
|
||||
{{/each}}
|
||||
<bottomEntry></bottomEntry> <!-- Intentionally blank, fixes issue what last message being cut off while scrolling -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="panel-footer">{{> chatInput}}</div>
|
||||
@ -21,7 +24,7 @@
|
||||
|
||||
<template name="chatInput">
|
||||
<div class="chat-input-wrapper">
|
||||
<input type="text" id="newMessageInput" placeholder="Write a message..." rel="tooltip" data-placement="top" title="Write a new message" />
|
||||
<textarea id="newMessageInput" placeholder="Write a message..." rel="tooltip" data-placement="top" title="Write a new message"></textarea>
|
||||
<button type="submit" id="sendMessageButton" class="btn" rel="tooltip" data-placement="top" title="Click to send your message">
|
||||
Send
|
||||
</button>
|
||||
@ -39,7 +42,7 @@
|
||||
<tr>
|
||||
<td>
|
||||
{{#if message.from_username}}
|
||||
<div class="userNameEntry" rel="tooltip" data-placement="bottom" title="{{message.from_username}}">
|
||||
<div {{messageFontSize}} class="userNameEntry" rel="tooltip" data-placement="bottom" title="{{message.from_username}}">
|
||||
<strong>{{message.from_username}}</strong>
|
||||
</div>
|
||||
{{/if}}
|
||||
@ -50,6 +53,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
<div style="color:{{colourToHex message.from_color}}">{{{sanitizeAndFormat message.message}}}</div> <!-- Messages must be safely filtered and stripped -->
|
||||
{{autoscroll}} <!-- Scroll to the bottom after inserting a new message -->
|
||||
</template>
|
||||
|
||||
<!-- Displays the list of options available -->
|
||||
|
@ -1,27 +1,32 @@
|
||||
<template name="displayUserIcons">
|
||||
<td>{{#if isUserSharingVideo user}}
|
||||
<span class="userListSettingIcon glyphicon glyphicon-facetime-video"></span>
|
||||
<span class="userListSettingIcon glyphicon glyphicon-facetime-video" rel="tooltip" data-placement="bottom" title="{{user.name}} is sharing their webcam"></span>
|
||||
{{/if}}</td>
|
||||
|
||||
<!-- Should use much more noticeable icons than just bootstrap's volume-up & volume-down to differentiate talking but it is good for now -->
|
||||
<td>{{#if isUserSharingAudio user}}
|
||||
{{#if isUserMuted user}}
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-off"></span>
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-off" rel="tooltip" data-placement="bottom" title="{{user.name}} is muted"></span>
|
||||
{{else}}
|
||||
{{#if isUserTalking user}}
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-up"></span>
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-up" rel="tooltip" data-placement="bottom" title="{{user.name}} is talking"></span>
|
||||
{{else}}
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-down"></span>
|
||||
<span class="userListSettingIcon glyphicon glyphicon-volume-down" rel="tooltip" data-placement="bottom" title="{{user.name}} is not talking"></span>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}</td>
|
||||
{{/if}}
|
||||
<!-- listen only: -->
|
||||
{{#if isUserListenOnly user}}
|
||||
<span class="userListSettingIcon glyphicon glyphicon-headphones" title="Listening only"></span>
|
||||
{{/if}}
|
||||
</td>
|
||||
|
||||
<td>{{#if user.presenter}}<span class="userListSettingIcon glyphicon glyphicon-picture"></span>{{/if}}</td>
|
||||
<td>{{#if user.presenter}}<span class="userListSettingIcon glyphicon glyphicon-picture" rel="tooltip" data-placement="bottom" title="{{user.name}} is the presenter"></span>{{/if}}</td>
|
||||
|
||||
<td>{{#if user.raise_hand}}<span class="userListSettingIcon glyphicon glyphicon-hand-up"></span>{{/if}}</td>
|
||||
<td>{{#if user.raise_hand}}<span class="userListSettingIcon glyphicon glyphicon-hand-up" rel="tooltip" data-placement="bottom" title="{{user.name}} has raised their hand"></span>{{/if}}</td>
|
||||
</template>
|
||||
|
||||
<template name="usernameEntry"> <!-- A template now because more information be added/styled -->
|
||||
<template name="usernameEntry">
|
||||
<td class="userNameContainer">
|
||||
{{#if isCurrentUser userId}}
|
||||
<p class="userNameEntry" rel="tooltip" data-placement="bottom" title="{{user.name}} (you)">
|
||||
|
@ -41,7 +41,8 @@ Template.slide.helpers
|
||||
shapeType = shapeInfo?.type
|
||||
|
||||
if shapeType isnt "text"
|
||||
for num in [0..3] # the coordinates must be in the range 0 to 1
|
||||
len = shapeInfo.points.length
|
||||
for num in [0..len] # the coordinates must be in the range 0 to 1
|
||||
shapeInfo?.points[num] = shapeInfo?.points[num] / 100
|
||||
wpm.makeShape(shapeType, shapeInfo)
|
||||
wpm.updateShape(shapeType, shapeInfo)
|
||||
@ -54,7 +55,8 @@ Template.shape.rendered = ->
|
||||
shapeType = shapeInfo?.type
|
||||
|
||||
if shapeType isnt "text"
|
||||
for num in [0..3] # the coordinates must be in the range 0 to 1
|
||||
len = shapeInfo.points.length
|
||||
for num in [0..len] # the coordinates must be in the range 0 to 1
|
||||
shapeInfo.points[num] = shapeInfo.points[num] / 100
|
||||
|
||||
wpm = Template.slide.whiteboardPaperModel
|
||||
|
@ -14,9 +14,6 @@ class @WhiteboardLineModel extends WhiteboardToolModel
|
||||
# format: svg path, stroke color, thickness
|
||||
@definition = ["", "#000", "0px"]
|
||||
|
||||
# @lineX = null
|
||||
# @lineY = null
|
||||
|
||||
# Creates a line in the paper
|
||||
# @param {number} x the x value of the line start point as a percentage of the original width
|
||||
# @param {number} y the y value of the line start point as a percentage of the original height
|
||||
@ -63,36 +60,12 @@ class @WhiteboardLineModel extends WhiteboardToolModel
|
||||
y2 = info.points[3]
|
||||
|
||||
if @obj?
|
||||
path = @_buildPath(info.points)
|
||||
|
||||
# if adding points from the pencil
|
||||
if _.isBoolean(info.adding)
|
||||
add = info.adding
|
||||
@definition[0] = path
|
||||
|
||||
pathPercent = "L" + x1 + " " + y1
|
||||
@definition.data[0] += pathPercent
|
||||
|
||||
x1 = x1 * @gw + @xOffset
|
||||
y1 = y1 * @gh + @yOffset
|
||||
|
||||
# if adding to the line
|
||||
if add
|
||||
path = @obj.attrs.path + "L" + x1 + " " + y1
|
||||
@obj.attr path: path
|
||||
|
||||
# if simply updating the last portion (for drawing a straight line)
|
||||
else
|
||||
@obj.attrs.path.pop()
|
||||
path = @obj.attrs.path.join(" ")
|
||||
path = path + "L" + x1 + " " + y1
|
||||
@obj.attr path: path
|
||||
|
||||
# adding lines from the line tool
|
||||
else
|
||||
path = @_buildPath(x1, y1, x2, y2)
|
||||
@definition[0] = path
|
||||
|
||||
path = @_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset)
|
||||
@obj.attr path: path
|
||||
path = @_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset)
|
||||
@obj.attr path: path
|
||||
|
||||
# Draw a line on the paper
|
||||
# @param {number,string} x1 1) the x value of the first point
|
||||
@ -110,19 +83,19 @@ class @WhiteboardLineModel extends WhiteboardToolModel
|
||||
draw: (x1, y1, x2, y2, colour, thickness) ->
|
||||
|
||||
# if the drawing is from the pencil tool, it comes as a path first
|
||||
if _.isString(x1)
|
||||
colour = y1
|
||||
thickness = x2
|
||||
path = x1
|
||||
# if _.isString(x1)
|
||||
# colour = y1
|
||||
# thickness = x2
|
||||
# path = x1
|
||||
|
||||
# if the drawing is from the line tool, it comes with two points
|
||||
else
|
||||
path = @_buildPath(x1, y1, x2, y2)
|
||||
# # if the drawing is from the line tool, it comes with two points
|
||||
# else
|
||||
# path = @_buildPath(points)
|
||||
|
||||
line = @paper.path(@_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset))
|
||||
line.attr Utils.strokeAndThickness(colour, thickness)
|
||||
line.attr({"stroke-linejoin": "round"})
|
||||
line
|
||||
# line = @paper.path(@_scaleLinePath(path, @gw, @gh, @xOffset, @yOffset))
|
||||
# line.attr Utils.strokeAndThickness(colour, thickness)
|
||||
# line.attr({"stroke-linejoin": "round"})
|
||||
# line
|
||||
|
||||
# When dragging for drawing lines starts
|
||||
# @param {number} x the x value of the cursor
|
||||
@ -185,8 +158,19 @@ class @WhiteboardLineModel extends WhiteboardToolModel
|
||||
# [ @_scaleLinePath(path.join(","), 1 / @gw, 1 / @gh),
|
||||
# @currentColour, @currentThickness ]
|
||||
|
||||
_buildPath: (x1, y1, x2, y2) ->
|
||||
"M#{x1} #{y1}L#{x2} #{y2}"
|
||||
_buildPath: (points) ->
|
||||
path = ""
|
||||
|
||||
if points and points.length >= 2
|
||||
path += "M #{points[0]} #{points[1]}"
|
||||
i = 2
|
||||
|
||||
while i < points.length
|
||||
path += "L#{points[i]} #{points[i + 1]}"
|
||||
i += 2
|
||||
|
||||
path += "Z"
|
||||
path
|
||||
|
||||
# Scales a path string to fit within a width and height of the new paper size
|
||||
# @param {number} w width of the shape as a percentage of the original width
|
||||
|
@ -9,24 +9,21 @@ class @WhiteboardPaperModel
|
||||
|
||||
# all slides in the presentation indexed by url
|
||||
@slides = {}
|
||||
# the slide being shown
|
||||
@currentSlide = null
|
||||
|
||||
@fitToPage = true
|
||||
@panX = null
|
||||
@panY = null
|
||||
|
||||
@current = {}
|
||||
|
||||
# the slide being shown
|
||||
@current.slide = null
|
||||
|
||||
# a raphaeljs set with all the shapes in the current slide
|
||||
@currentShapes = null
|
||||
@current.shapes = null
|
||||
# a list of shapes as passed to this client when it receives `all_slides`
|
||||
# (se we are able to redraw the shapes whenever needed)
|
||||
@currentShapesDefinitions = []
|
||||
# pointers to the current shapes being drawn
|
||||
@currentLine = null
|
||||
@currentRect = null
|
||||
@currentEllipse = null
|
||||
@currentTriangle = null
|
||||
@currentText = null
|
||||
@current.shapeDefinitions = []
|
||||
|
||||
@zoomLevel = 1
|
||||
@shiftPressed = false
|
||||
@ -100,7 +97,7 @@ class @WhiteboardPaperModel
|
||||
# Re-add the images to the paper that are found
|
||||
# in the slides array (an object of urls and dimensions).
|
||||
rebuild: ->
|
||||
@currentSlide = null
|
||||
@current.slide = null
|
||||
for url of @slides
|
||||
if @slides.hasOwnProperty(url)
|
||||
@addImageToPaper url, @slides[url].getWidth(), @slides[url].getHeight()
|
||||
@ -116,13 +113,13 @@ class @WhiteboardPaperModel
|
||||
# will change quite a bit
|
||||
# slides
|
||||
slidesTmp = _.clone(@slides)
|
||||
urlTmp = @currentSlide
|
||||
urlTmp = @current.slide
|
||||
@removeAllImagesFromPaper()
|
||||
@slides = slidesTmp
|
||||
@rebuild()
|
||||
@showImageFromPaper(urlTmp?.url)
|
||||
# drawings
|
||||
tmp = _.clone(@currentShapesDefinitions)
|
||||
tmp = _.clone(@current.shapeDefinitions)
|
||||
@clearShapes()
|
||||
@drawListOfShapes(tmp)
|
||||
|
||||
@ -167,10 +164,10 @@ class @WhiteboardPaperModel
|
||||
# y-offset from top left corner as percentage of original height of paper
|
||||
@slides[url] = new WhiteboardSlideModel(img.id, url, img, originalWidth, originalHeight, sw, sh, cx, cy)
|
||||
|
||||
unless @currentSlide?
|
||||
unless @current.slide?
|
||||
img.toBack()
|
||||
@currentSlide = @slides[url]
|
||||
else if @currentSlide.url is url
|
||||
@current.slide = @slides[url]
|
||||
else if @current.slide.url is url
|
||||
img.toBack()
|
||||
else
|
||||
img.hide()
|
||||
@ -191,7 +188,7 @@ class @WhiteboardPaperModel
|
||||
@raphaelObj.getById(@slides[url]?.getId())?.remove()
|
||||
#@trigger('paper:image:removed', @slides[url].getId()) # TODO do we need this?
|
||||
@slides = {}
|
||||
@currentSlide = null
|
||||
@current.slide = null
|
||||
|
||||
# Shows an image from the paper.
|
||||
# The url must be in the slides array.
|
||||
@ -199,16 +196,16 @@ class @WhiteboardPaperModel
|
||||
showImageFromPaper: (url) ->
|
||||
# TODO: temporary solution
|
||||
url = @_slideUrl(url)
|
||||
if not @currentSlide? or (@slides[url]? and @currentSlide.url isnt url)
|
||||
@_hideImageFromPaper(@currentSlide.url) if @currentSlide?
|
||||
if not @current.slide? or (@slides[url]? and @current.slide.url isnt url)
|
||||
@_hideImageFromPaper(@current.slide.url) if @current.slide?
|
||||
next = @_getImageFromPaper(url)
|
||||
if next
|
||||
next.show()
|
||||
next.toFront()
|
||||
@currentShapes.forEach (element) ->
|
||||
@current.shapes.forEach (element) ->
|
||||
element.toFront()
|
||||
@cursor.toFront()
|
||||
@currentSlide = @slides[url]
|
||||
@current.slide = @slides[url]
|
||||
|
||||
# Updates the paper from the server values.
|
||||
# @param {number} cx_ the x-offset value as a percentage of the original width
|
||||
@ -256,14 +253,14 @@ class @WhiteboardPaperModel
|
||||
@currentTool = tool
|
||||
console.log "setting current tool to", tool
|
||||
switch tool
|
||||
when "path", "line"
|
||||
when "line"
|
||||
@cursor.undrag()
|
||||
@currentLine = @_createTool(tool)
|
||||
@cursor.drag(@currentLine.dragOnMove, @currentLine.dragOnStart, @currentLine.dragOnEnd)
|
||||
when "rect"
|
||||
@current.line = @_createTool(tool)
|
||||
@cursor.drag(@current.line.dragOnMove, @current.line.dragOnStart, @current.line.dragOnEnd)
|
||||
when "rectangle"
|
||||
@cursor.undrag()
|
||||
@currentRect = @_createTool(tool)
|
||||
@cursor.drag(@currentRect.dragOnMove, @currentRect.dragOnStart, @currentRect.dragOnEnd)
|
||||
@current.rectangle = @_createTool(tool)
|
||||
@cursor.drag(@current.rectangle.dragOnMove, @current.rectangle.dragOnStart, @current.rectangle.dragOnEnd)
|
||||
|
||||
# TODO: the shapes below are still in the old format
|
||||
# when "panzoom"
|
||||
@ -333,15 +330,15 @@ class @WhiteboardPaperModel
|
||||
# Draws an array of shapes to the paper.
|
||||
# @param {array} shapes the array of shapes to draw
|
||||
drawListOfShapes: (shapes) ->
|
||||
@currentShapesDefinitions = shapes
|
||||
@currentShapes = @raphaelObj.set()
|
||||
@current.shapeDefinitions = shapes
|
||||
@current.shapes = @raphaelObj.set()
|
||||
for shape in shapes
|
||||
shapeType = shape?.shape?.shape_type
|
||||
dataBlock = shape?.shape?.shape
|
||||
data = if _.isString(dataBlock) then JSON.parse(dataBlock) else dataBlock
|
||||
tool = @_createTool(shapeType)
|
||||
if tool?
|
||||
@currentShapes.push tool.draw.apply(tool, data)
|
||||
@current.shapes.push tool.draw.apply(tool, data)
|
||||
else
|
||||
console.log "shape not recognized at drawListOfShapes", shape
|
||||
|
||||
@ -355,8 +352,8 @@ class @WhiteboardPaperModel
|
||||
|
||||
# Clear all shapes from this paper.
|
||||
clearShapes: ->
|
||||
if @currentShapes?
|
||||
@currentShapes.forEach (element) ->
|
||||
if @current.shapes?
|
||||
@current.shapes.forEach (element) ->
|
||||
element.remove()
|
||||
@currentShapes = []
|
||||
@currentShapesDefinitions = []
|
||||
@ -368,52 +365,20 @@ class @WhiteboardPaperModel
|
||||
# Updated a shape `shape` with the data in `data`.
|
||||
# TODO: check if the objects exist before calling update, if they don't they should be created
|
||||
updateShape: (shape, data) ->
|
||||
switch shape
|
||||
when "line"
|
||||
@currentLine.update(data)
|
||||
when "rectangle"
|
||||
@currentRect.update(data)
|
||||
when "ellipse"
|
||||
@currentEllipse.update(data)
|
||||
when "triangle"
|
||||
@currentTriangle.update(data)
|
||||
when "text"
|
||||
#@currentText.update.apply(@currentText, data)
|
||||
@currentText.update(data)
|
||||
else
|
||||
console.log "shape not recognized at updateShape", shape
|
||||
@current[shape].update(data)
|
||||
|
||||
# Make a shape `shape` with the data in `data`.
|
||||
makeShape: (shape, data) ->
|
||||
tool = null
|
||||
switch shape
|
||||
when "path", "line"
|
||||
@currentLine = @_createTool(shape)
|
||||
toolModel = @currentLine
|
||||
tool = @currentLine.make(data)
|
||||
when "rectangle"
|
||||
@currentRect = @_createTool(shape)
|
||||
toolModel = @currentRect
|
||||
tool = @currentRect.make(data)
|
||||
when "ellipse"
|
||||
@currentEllipse = @_createTool(shape)
|
||||
toolModel = @currentEllipse
|
||||
tool = @currentEllipse.make(data)
|
||||
when "triangle"
|
||||
@currentTriangle = @_createTool(shape)
|
||||
toolModel = @currentTriangle
|
||||
tool = @currentTriangle.make(data)
|
||||
when "text"
|
||||
@currentText = @_createTool(shape)
|
||||
toolModel = @currentText
|
||||
#tool = @currentText.make.apply(@currentText, data)
|
||||
tool = @currentText.make(data)
|
||||
else
|
||||
console.log "shape not recognized at makeShape", shape
|
||||
|
||||
@current[shape] = @_createTool(shape)
|
||||
toolModel = @current[shape]
|
||||
tool = @current[shape].make(data)
|
||||
|
||||
if tool?
|
||||
@currentShapes ?= @raphaelObj.set()
|
||||
@currentShapes.push(tool)
|
||||
@currentShapesDefinitions.push(toolModel.getDefinition())
|
||||
@current.shapes ?= @raphaelObj.set()
|
||||
@current.shapes.push(tool)
|
||||
@current.shapeDefinitions.push(toolModel.getDefinition())
|
||||
|
||||
# Update the cursor position on screen
|
||||
# @param {number} x the x value of the cursor as a percentage of the width
|
||||
@ -448,8 +413,8 @@ class @WhiteboardPaperModel
|
||||
|
||||
#get the actual size of the slide, depending on the limiting factor (container width or container height)
|
||||
|
||||
actualWidth = @currentSlide.displayWidth
|
||||
actualHeight = @currentSlide.displayHeight
|
||||
actualWidth = @current.slide.displayWidth
|
||||
actualHeight = @current.slide.displayHeight
|
||||
#console.log("actualWidth:" + actualWidth + " actualHeight: " + actualHeight)
|
||||
|
||||
#calculate parameters to pass
|
||||
@ -468,7 +433,6 @@ class @WhiteboardPaperModel
|
||||
#set parameters to raphael viewbox
|
||||
@raphaelObj.setViewBox(newXPos , newyPos, newWidth , newHeight , true)
|
||||
|
||||
|
||||
# update the rectangle elements which create the border when page is zoomed
|
||||
@borders.left.attr( {width:newXPos, height: @containerHeight} )
|
||||
|
||||
@ -743,17 +707,19 @@ class @WhiteboardPaperModel
|
||||
@shiftPressed = false
|
||||
|
||||
_currentSlideDimensions: ->
|
||||
if @currentSlide? then @currentSlide.getDimensions() else [0, 0]
|
||||
if @current.slide? then @current.slide.getDimensions() else [0, 0]
|
||||
|
||||
_currentSlideOriginalDimensions: ->
|
||||
if @currentSlide? then @currentSlide.getOriginalDimensions() else [0, 0]
|
||||
if @current.slide? then @current.slide.getOriginalDimensions() else [0, 0]
|
||||
|
||||
_currentSlideOffsets: ->
|
||||
if @currentSlide? then @currentSlide.getOffsets() else [0, 0]
|
||||
if @current.slide? then @current.slide.getOffsets() else [0, 0]
|
||||
|
||||
# Wrapper method to create a tool for the whiteboard
|
||||
_createTool: (type) ->
|
||||
switch type
|
||||
when "pencil"
|
||||
model = WhiteboardLineModel
|
||||
when "path", "line"
|
||||
model = WhiteboardLineModel
|
||||
when "rectangle"
|
||||
|
@ -1,6 +1,6 @@
|
||||
Meteor.methods
|
||||
addChatToCollection: (meetingId, messageObject) ->
|
||||
# manually convert time from 1.408645053653E12 to 1408645053653 if necessary
|
||||
# manually convert time from 1.408645053653E12 to 1408645053653 if necessary (this is the time_from that the Flash client outputs)
|
||||
messageObject.from_time = (messageObject.from_time).toString().split('.').join("").split("E")[0]
|
||||
entry =
|
||||
meetingId: meetingId
|
||||
|
@ -29,7 +29,9 @@ Meteor.methods
|
||||
console.log "added textShape id =[#{id}]:#{shapeObject.id} in #{meetingId} || now there are #{numShapesOnSlide} shapes on the slide"
|
||||
|
||||
else
|
||||
if shapeObject?.status is "DRAW_END" #the mouse button was released - the drawing is complete
|
||||
# the mouse button was released - the drawing is complete
|
||||
# TODO: pencil messages currently don't send draw_end and are labeled all as DRAW_START
|
||||
if shapeObject?.status is "DRAW_END" or (shapeObject?.status is "DRAW_START" and shapeObject?.shape_type is "pencil")
|
||||
entry =
|
||||
meetingId: meetingId
|
||||
whiteboardId: whiteboardId
|
||||
|
@ -39,6 +39,7 @@ Meteor.methods
|
||||
extern_userid: user.extern_userid
|
||||
permissions: user.permissions
|
||||
locked: user.locked
|
||||
time_of_joining: user.timeOfJoining
|
||||
voiceUser:
|
||||
web_userid: user.voiceUser.web_userid
|
||||
callernum: user.voiceUser.callernum
|
||||
|
@ -168,48 +168,37 @@ class Meteor.RedisPubSub
|
||||
]
|
||||
|
||||
unless message.header?.name in ignoredEventTypes
|
||||
#console.log "\nchannel=" + channel
|
||||
#console.log "correlationId=" + correlationId if correlationId?
|
||||
console.log "eventType=" + message.header?.name #+ "\n"
|
||||
#log.debug({ pattern: pattern, channel: channel, message: message}, "Received a message from redis")
|
||||
console.log jsonMsg
|
||||
|
||||
if message.header?.name is 'user_voice_talking_message'
|
||||
u = Meteor.Users.findOne({'userId': message.payload?.user?.userid, 'meetingId': meetingId})
|
||||
if u?
|
||||
if not u?.user?.voiceUser?.muted
|
||||
console.log "setting talking to #{message?.payload?.user?.voiceUser?.talking}"
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.talking':message?.payload?.user?.voiceUser?.talking}})
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.joined':true}})
|
||||
else
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.talking':false}})
|
||||
# handle voice events
|
||||
if message.header?.name in ['user_left_voice_message', 'user_joined_voice_message', 'user_voice_talking_message', 'user_voice_muted_message']
|
||||
voiceUser = message.payload?.user?.voiceUser
|
||||
@updateVoiceUser(meetingId, voiceUser)
|
||||
|
||||
if message.header?.name is 'user_voice_muted_message'
|
||||
u = Meteor.Users.findOne({'userId': message.payload?.user?.userid, 'meetingId': meetingId})
|
||||
console.log "\n\n\n got a muted event and the user is #{u}"
|
||||
|
||||
if u?
|
||||
# make sure the user is not currently in talking mode
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.talking': false}})
|
||||
# update to muted
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.muted':message?.payload?.user?.voiceUser?.muted}})
|
||||
else console.log "ERROR!! did not find a matching user to mute!!"
|
||||
# listen only
|
||||
if message.header?.name is 'user_listening_only'
|
||||
u = Meteor.Users.findOne({userId: message.payload?.userid, meetingId: meetingId})
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.listenOnly':message.payload?.listen_only}})
|
||||
# most likely we don't need to ensure that the user's voiceUser's {talking, joined, muted, locked} are false by default #TODO?
|
||||
|
||||
if message.header?.name is "get_all_meetings_reply"
|
||||
console.log "Let's store some data for the running meetings so that when an HTML5 client joins everything is ready!"
|
||||
listOfMeetings = message.payload?.meetings
|
||||
for meeting in listOfMeetings
|
||||
# we currently do not have voice_conf or duration in this message.
|
||||
Meteor.call("addMeetingToCollection", meeting.meetingID, meeting.meetingName, meeting.recorded, "", "")
|
||||
Meteor.call("addMeetingToCollection", meeting.meetingID, meeting.meetingName, meeting.recorded, meeting.voiceBridge, meeting.duration)
|
||||
|
||||
if message.header?.name is "get_users_reply" and message.payload?.requester_id is "nodeJSapp"
|
||||
unless Meteor.Meetings.findOne({MeetingId: message.payload?.meeting_id})?
|
||||
users = message.payload?.users
|
||||
for user in users
|
||||
user.timeOfJoining = message.header?.current_time # TODO this might need to be removed
|
||||
Meteor.call("addUserToCollection", meetingId, user)
|
||||
|
||||
if message.header?.name is "user_joined_message"
|
||||
user = message.payload.user
|
||||
user.timeOfJoining = message.header?.current_time
|
||||
Meteor.call("addUserToCollection", meetingId, user)
|
||||
|
||||
if message.header?.name is "user_left_message"
|
||||
@ -218,12 +207,16 @@ class Meteor.RedisPubSub
|
||||
Meteor.call("removeUserFromCollection", meetingId, userId)
|
||||
|
||||
if message.header?.name is "get_chat_history_reply" and message.payload?.requester_id is "nodeJSapp"
|
||||
unless Meteor.Meetings.findOne({MeetingId: message.payload?.meeting_id})? # TODO check if MeetingId or meetingId!!
|
||||
unless Meteor.Meetings.findOne({MeetingId: message.payload?.meeting_id})?
|
||||
for chatMessage in message.payload?.chat_history
|
||||
Meteor.call("addChatToCollection", meetingId, chatMessage)
|
||||
|
||||
if message.header?.name is "send_public_chat_message" or message.header?.name is "send_private_chat_message"
|
||||
messageObject = message.payload?.message
|
||||
|
||||
# use current_time instead of message.from_time so that the chats from Flash and HTML5 have uniform times
|
||||
messageObject.from_time = message.header?.current_time
|
||||
|
||||
Meteor.call("addChatToCollection", meetingId, messageObject)
|
||||
|
||||
if message.header?.name is "meeting_created_message"
|
||||
@ -349,6 +342,18 @@ class Meteor.RedisPubSub
|
||||
unless message.header?.name is "disconnect_all_users_message"
|
||||
Meteor.call("removeMeetingFromCollection", meetingId)
|
||||
|
||||
#update a voiceUser
|
||||
updateVoiceUser: (meetingId, voiceUserObject) ->
|
||||
console.log "I am updating the voiceUserObject with the following: " + JSON.stringify voiceUserObject
|
||||
u = Meteor.Users.findOne({userId: voiceUserObject?.web_userid, meetingId: meetingId})
|
||||
if u?
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.talking':voiceUserObject?.talking}})# talking
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.joined':voiceUserObject?.joined}})# joined
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.locked':voiceUserObject?.locked}})# locked
|
||||
Meteor.Users.update({_id:u._id}, {$set: {'user.voiceUser.muted':voiceUserObject?.muted}})# muted
|
||||
else
|
||||
console.log "ERROR! did not find such voiceUser!"
|
||||
|
||||
# message should be an object
|
||||
publish: (channel, message) ->
|
||||
console.log "Publishing channel=#{channel}, message=#{JSON.stringify(message)}"
|
||||
@ -385,3 +390,4 @@ class Meteor.RedisPubSub
|
||||
"payload": {} # I need this, otherwise bbb-apps won't recognize the message
|
||||
|
||||
@pubClient.publish(Meteor.config.redis.channels.toBBBApps.meeting, JSON.stringify (message))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user