Merge branch 'creating-better-templates' of github.com:perroned/bigbluebutton into merge-danny-ui

Conflicts:
	labs/meteor-client/client/views/chat/chat_bar.coffee
	labs/meteor-client/client/views/users/user_item.html
This commit is contained in:
Anton Georgiev 2014-09-22 19:35:53 +00:00
commit 626e91fe1c
7 changed files with 263 additions and 217 deletions

View File

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

View File

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

View File

@ -1,3 +1,8 @@
bottomEntry{
border: none;
padding-bottom: 0px;
padding-top: 0px;
}
.chat{
list-style: none;
margin: 0px;

View File

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

View File

@ -1,188 +1,214 @@
@sendMessage = ->
message = linkify $('#newMessageInput').val() # get the message from the input box
unless (message?.length > 0 and (/\S/.test(message))) # check the message has content and it is not whitespace
return # do nothing if invalid message
chattingWith = getInSession('inChatWith')
if chattingWith isnt "PUBLIC_CHAT"
dest = Meteor.Users.findOne("userId": chattingWith)
messageForServer = { # construct message for server
"message": message
"chat_type": if chattingWith is "PUBLIC_CHAT" then "PUBLIC_CHAT" else "PRIVATE_CHAT"
"from_userid": getInSession("userId")
"from_username": getUsersName()
"from_tz_offset": "240"
"to_username": if chattingWith is "PUBLIC_CHAT" then "public_chat_username" else dest.user.name
"to_userid": if chattingWith is "PUBLIC_CHAT" then "public_chat_userid" else chattingWith
"from_lang": "en"
"from_time": getTime()
"from_color": "0x000000"
# "from_color": "0x#{getInSession("messageColor")}"
}
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer
$('#newMessageInput').val '' # Clear message box
Template.chatInput.events
'click #sendMessageButton': (event) ->
sendMessage()
'keypress #newMessageInput': (event) -> # user pressed a button inside the chatbox
if event.which is 13 # Check for pressing enter to submit message
sendMessage()
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'}."
# 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(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': 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
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)
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")
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()
Template.optionsFontSize.events
"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
setInSession 'inChatWith', 'PUBLIC_CHAT'
setInSession 'display_chatPane', true
Meteor.call("deletePrivateChatMessages", getInSession("userId"), @userId)
return false # stops propogation/prevents default
'click .optionsChatTab': (event) ->
setInSession 'display_chatPane', false
'click .privateChatTab': (event) ->
setInSession 'display_chatPane', true
console.log ".private"
'click .publicChatTab': (event) ->
setInSession 'display_chatPane', true
'click .tab': (event) ->
setInSession "inChatWith", @userId
Template.tabButtons.helpers
getChatbarTabs: ->
tabs = makeTabs()
makeTabButton: -> # create tab button for private chat or other such as options
safeClass = @class.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
safeName = @name.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;')
button = '<li '
button += 'class="'
button += 'active ' if getInSession("inChatWith") is @userId
button += "tab #{safeClass}\"><a href=\"#\" data-toggle=\"tab\">#{safeName}"
button += '&nbsp;<button class="close closeTab" type="button" >×</button>' if @class is 'privateChatTab'
button += '</a></li>'
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="'
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, '&amp;').replace(/<(?![au\/])/g, '&lt;').replace(/\/([^au])>/g, '$1&gt;').replace(/([^=])"(?!>)/g, '$1&quot;')
res = Template.message.toClickable res
res = Template.message.activateBreakLines res
@sendMessage = ->
message = linkify $('#newMessageInput').val() # get the message from the input box
unless (message?.length > 0 and (/\S/.test(message))) # check the message has content and it is not whitespace
return # do nothing if invalid message
chattingWith = getInSession('inChatWith')
if chattingWith isnt "PUBLIC_CHAT"
dest = Meteor.Users.findOne("userId": chattingWith)
messageForServer = { # construct message for server
"message": message
"chat_type": if chattingWith is "PUBLIC_CHAT" then "PUBLIC_CHAT" else "PRIVATE_CHAT"
"from_userid": getInSession("userId")
"from_username": getUsersName()
"from_tz_offset": "240"
"to_username": if chattingWith is "PUBLIC_CHAT" then "public_chat_username" else dest.user.name
"to_userid": if chattingWith is "PUBLIC_CHAT" then "public_chat_userid" else chattingWith
"from_lang": "en"
"from_time": getTime()
"from_color": "0x000000"
# "from_color": "0x#{getInSession("messageColor")}"
}
Meteor.call "sendChatMessagetoServer", getInSession("meetingId"), messageForServer
$('#newMessageInput').val '' # Clear message box
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'}."
# 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(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': 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
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")
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()
Template.optionsFontSize.events
"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
setInSession 'inChatWith', 'PUBLIC_CHAT'
setInSession 'display_chatPane', true
Meteor.call("deletePrivateChatMessages", getInSession("userId"), @userId)
return false # stops propogation/prevents default
'click .optionsChatTab': (event) ->
setInSession 'display_chatPane', false
'click .privateChatTab': (event) ->
setInSession 'display_chatPane', true
console.log ".private"
'click .publicChatTab': (event) ->
setInSession 'display_chatPane', true
'click .tab': (event) ->
setInSession "inChatWith", @userId
Template.tabButtons.helpers
getChatbarTabs: ->
tabs = makeTabs()
makeTabButton: -> # create tab button for private chat or other such as options
safeClass = @class.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
safeName = @name.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
button = '<li '
button += 'class="'
button += 'active ' if getInSession("inChatWith") is @userId
button += "tab #{safeClass}\"><a href=\"#\" data-toggle=\"tab\">#{safeName}"
button += '&nbsp;<button class="close closeTab" type="button" >×</button>' if @class is 'privateChatTab'
button += '</a></li>'
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="'
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, '&amp;').replace(/<(?![au\/])/g, '&lt;').replace(/\/([^au])>/g, '$1&gt;').replace(/([^=])"(?!>)/g, '$1&quot;');
res = Template.message.toClickable res
res = Template.message.activateBreakLines res

View File

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

View File

@ -1,18 +1,17 @@
<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}}
<td>{{#if isUserSharingAudio user}}
{{#if isUserMuted user}}
<span class="userListSettingIcon glyphicon glyphicon-volume-off" title="Muted"></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}}
@ -22,12 +21,12 @@
{{/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)">