1
0
mirror of https://github.com/wbbaddons/Tims-Chat.git synced 2024-10-31 14:10:08 +00:00

Rewrite frontend

With this commit pretty much the whole frontend has been rewritten.
This should hopefully fix most of the issues in “exotic” styles.

The changes include:

- Overall cleanup
- Better adaption to the WCF design
- Reworked private channels menu
- Reworked sidebar (may change later, currently the heights of the elements are hardcoded and not dynamic)
- Working user action menu
- Fixed auto scrolling
- Better visual notifications

- This software does not contain any easter eggs
- No penguins were harmed in the production of this software

Todo:

- Fullscreen mode
- Some language variables
This commit is contained in:
Maximilian Mader 2014-02-08 03:21:25 +01:00
parent 4d208f8893
commit 92ed075076
8 changed files with 762 additions and 654 deletions

View File

@ -6,7 +6,7 @@ everything that happens in the GUI of **Tims Chat**.
### Copyright Information
# @author Tim Düsterhus
# @copyright 2010-2013 Tim Düsterhus
# @copyright 2010-2014 Tim Düsterhus
# @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
# @package be.bastelstu.chat
###
@ -73,7 +73,7 @@ exposed by a function if necessary.
Initialize **Tims Chat**. Bind needed DOM events and initialize data structures.
initialized = false
init = (roomID, config, titleTemplate, messageTemplate, userTemplate) ->
init = (roomID, config, titleTemplate, messageTemplate, userTemplate, userMenuTemplate) ->
return false if initialized
initialized = true
@ -81,6 +81,7 @@ Initialize **Tims Chat**. Bind needed DOM events and initialize data structures.
v.titleTemplate = titleTemplate
v.messageTemplate = messageTemplate
v.userTemplate = userTemplate
v.userMenuTemplate = userMenuTemplate
console.log 'Initializing'
@ -117,7 +118,8 @@ Insert the appropriate smiley code into the input when a smiley is clicked.
Handle private channel menu
$('#privateChannelsMenu').on 'click', '.privateChannel', -> openPrivateChannel $(@).data 'privateChannelID'
$('#timsChatMessageTabMenu > .tabMenu').on 'click', '.timsChatMessageTabMenuAnchor', ->
openPrivateChannel $(@).data 'userID'
Handle submitting the form. The message will be validated by some basic checks, passed to the `submit` eventlisteners
and afterwards sent to the server by an AJAX request.
@ -217,9 +219,24 @@ Reset autocompleter to default status, when the input is `click`ed, as the posit
value: null
caret: null
Bind user menu functions
$('#dropdownMenuContainer').on 'click', '.jsTimsChatUserMenuWhisper', ->
command = "/whisper #{userList.current[$(@).parents('ul').data 'userID'].username}, "
return if $('#timsChatInput').val().match(new RegExp WCF.String.escapeRegExp("^#{command}"), 'i')
insertText command, prepend: yes
$('#dropdownMenuContainer').on 'click', '.jsTimsChatUserMenuQuery', -> openPrivateChannel $(@).parents('ul').data 'userID'
$('#dropdownMenuContainer').on 'click', '.jsTimsChatUserMenuBan', ->
command = "/ban #{userList.current[$(@).parents('ul').data 'userID'].username}, "
return if $('#timsChatInput').val().match(new RegExp WCF.String.escapeRegExp("^#{command}"), 'i')
insertText command, prepend: yes
Refresh the room list when the associated button is `click`ed.
$('#timsChatRoomList button').click -> do refreshRoomList
$('#timsChatRoomListReloadButton').click -> do refreshRoomList
Clear the chat by removing every single message once the clear button is `clicked`.
@ -247,9 +264,9 @@ Mark smilies as disabled when they are disabled.
$('#timsChatSmilies').click (event) ->
if $(@).data 'status'
$('#smilies').removeClass 'disabled'
$('#smilies').removeClass 'invisible'
else
$('#smilies').addClass 'disabled'
$('#smilies').addClass 'invisible'
Toggle fullscreen mode.
@ -272,11 +289,11 @@ Toggle checkboxes.
Hide topic container.
$('.jsTopicCloser').on 'click', ->
if $('.timsChatMessageContainer.active').data('userID') is 0
$('#timsChatTopic').addClass 'hidden'
else
closePrivateChannel $('.timsChatMessageContainer.active').data('userID')
$('#timsChatTopicCloser').on 'click', -> $('#timsChatTopic').addClass 'invisible'
Close private channels
$('#timsChatMessageTabMenu').on 'click', '.jsChannelCloser', -> closePrivateChannel $(@).parent().data 'userID'
Visibly mark the message once the associated checkbox is checked.
@ -289,27 +306,20 @@ Visibly mark the message once the associated checkbox is checked.
Scroll down when autoscroll is being activated.
$('#timsChatAutoscroll').click (event) ->
if $('#timsChatAutoscroll').data 'status'
if $(@).data 'status'
$('.timsChatMessageContainer.active').scrollTop $('.timsChatMessageContainer.active').prop 'scrollHeight'
$('.timsChatMessageContainer.active').on 'scroll', (event) ->
event.stopPropagation();
element = $ @
scrollTop = element.scrollTop()
scrollHeight = element.prop 'scrollHeight'
height = element.height()
if scrollTop < scrollHeight - height - 25
if $('#timsChatAutoscroll').data('status') is 1
scrollUpNotifications = on
do $('#timsChatAutoscroll').click
if scrollTop > scrollHeight - height - 10
if $('#timsChatAutoscroll').data('status') is 0
scrollUpNotifications = off
$(@).removeClass 'notification'
do $('#timsChatAutoscroll').click
$("#timsChatMessageTabMenu > .tabMenu > ul > li.ui-state-active").removeClass 'notify'
$(".timsChatMessageContainer.active").removeClass 'notify'
else
scrollUpNotifications = on
Bind scroll event on predefined message containers
$('.timsChatMessageContainer.active').on 'scroll', (event) ->
do event.stopPropagation
handleScroll event
Enable duplicate tab detection.
@ -384,35 +394,85 @@ Free the fish.
freeTheFish = ->
return if $.wcfIsset 'fish'
console.warn 'Freeing the fish'
fish = $ """<div id="fish">#{WCF.String.escapeHTML('><((((\u00B0>')}</div>"""
fish = $ """<div id="fish"><span></span></div>"""
fish.direction = 'right'
fish.css
position: 'fixed'
top: '50%'
left: '50%'
color: 'black'
textShadow: '1px 1px white'
zIndex: 9999
zIndex: 0x7FFFFFFF
textShadow: '1px 1px rgb(0, 0, 0)'
fish.appendTo $ 'body'
pe.fish = new WCF.PeriodicalExecuter ->
left = Math.random() * 100 - 50
top = Math.random() * 100 - 50
fish = $ '#fish'
left *= -1 unless fish.width() < (fish.position().left + left) < ($(window).width() - fish.width())
top *= -1 unless fish.height() < (fish.position().top + top) < ($(window).height() - fish.height())
fish.colors = ['78C5D6', '459ba8', '79C267', 'C5D647', 'F5D63D', 'F28C33', 'E868A2', 'BF62A6']
fish.colorIndex = 0
if left > 0
fish.text '><((((\u00B0>' if left > 0
else if left < 0
fish.text '<\u00B0))))><'
fish.texts =
right: '><((((\u00B0>'
left: '<\u00B0))))><'
fish.fishes = {}
Pre build fishes, this allows for faster animation
$.each fish.texts, (key, value) ->
fish.fishes[key] = []
index = 0
while index < value.length
html = $ '<span/>'
i = 0
$(value.split '').each (key, value) ->
$("<span>#{value}</span>").css
color: '#' + fish.colors[(i++ + index) % fish.colors.length]
textShadow: '1px 1px rgb(0, 0, 0)'
.appendTo html
fish.fishes[key][index++] = html
return
fish.find('> span').replaceWith fish.fishes[fish.direction][0]
fish.updateRainbowText = (key, value) ->
key = key || fish.direction
return unless fish.fishes[key]? || not fish.texts[key]?
value = value || fish.colorIndex++ % fish.texts[key].length
fish.find('> span').replaceWith fish.fishes[key][value]
fish.pePos = new WCF.PeriodicalExecuter ->
loops = 0
loop
++loops
left = Math.random() * 300 - 150
top = Math.random() * 300 - 150
if (fish.position().top + top) > 0 and (fish.position().left + left + fish.width()) < $(window).width() and (fish.position().top + top + fish.height()) < $(window).height() and (fish.position().left + left) > 0
break
else if loops is 10
console.log 'Magicarp used Splash for the 10th time in a row - it fainted!'
fish.css
'top': '50%'
'left': '50%'
break
if left > 0 and fish.text() isnt '><((((\u00B0>'
fish.direction = 'right'
fish.updateRainbowText null, fish.colorIndex % fish.texts.right.length
else if left < 0 and fish.text() isnt '<\u00B0))))><'
fish.direction = 'left'
fish.updateRainbowText null, fish.colorIndex % fish.texts.left.length
fish.animate
top: (fish.position().top + top)
left: (fish.position().left + left)
, 1e3
, 1.5e3
, 1.2e3
fish.peColor = new WCF.PeriodicalExecuter ->
do fish.updateRainbowText
, .125e3
Fetch new messages from the server and pass them to `handleMessages`. The userlist will be passed to `handleUsers`.
`remainingFailures` will be decreased on failure and message loading will be entirely disabled once it reaches zero.
@ -446,15 +506,13 @@ Prevent loading messages in parallel.
Insert the given messages into the chat stream.
handleMessages = (messages) ->
$('.timsChatMessageContainer.active').trigger 'scroll'
for message in messages
message.isInPrivateChannel = (String(message.type) is v.config.messageTypes.WHISPER) and ($.wcfIsset("timsChatMessageContainer#{message.receiver}") or $.wcfIsset("timsChatMessageContainer#{message.sender}"))
events.newMessage.fire message
createNewMessage = yes
if $('.timsChatMessage:last-child .text').is('ul') and lastMessage isnt null and lastMessage.type in [ 0, 7 ]
if $('.timsChatMessage:last-child .timsChatText').is('ul') and lastMessage isnt null and lastMessage.type in [ v.config.messageTypes.NORMAL, v.config.messageTypes.WHISPER ]
if lastMessage.type is message.type and lastMessage.sender is message.sender and lastMessage.receiver is message.receiver and lastMessage.isInPrivateChannel is message.isInPrivateChannel
createNewMessage = no
@ -490,11 +548,34 @@ Insert the given messages into the chat stream.
else
messageContainerID = 0
$("#timsChatMessageContainer#{messageContainerID} .timsChatMessage:last-child .text").append $(output).find('.text li:last-child')
$("#timsChatMessageContainer#{messageContainerID} .timsChatMessage:last-child .timsChatText").append $(output).find('.timsChatText li:last-child')
lastMessage = message
$('.timsChatMessageContainer.active').scrollTop $('.timsChatMessageContainer.active').prop('scrollHeight') if $('#timsChatAutoscroll').data('status') is 1
Handles scroll event of message containers
handleScroll = (event) ->
element = $ event.target
if element.hasClass 'active'
scrollTop = element.scrollTop()
scrollHeight = element.prop 'scrollHeight'
height = element.innerHeight()
if scrollTop < scrollHeight - height - 25
if $('#timsChatAutoscroll').data('status') is 1
scrollUpNotifications = on
do $('#timsChatAutoscroll').click
if scrollTop > scrollHeight - height - 10
if $('#timsChatAutoscroll').data('status') is 0
scrollUpNotifications = off
$("#timsChatMessageTabMenu > .tabMenu > ul > li.ui-state-active").removeClass 'notify'
$(".timsChatMessageContainer.active").removeClass 'notify'
do $('#timsChatAutoscroll').click
Rebuild the userlist based on the given `users`.
handleUsers = (users) ->
@ -545,14 +626,7 @@ Build HTML of the user and insert it into the list, if the users was not found i
li.append v.userTemplate.fetch user
menu = $ '<ul></ul>'
unless user.userID is WCF.User.userID
menu.append $("<li><a>#{WCF.Language.get('chat.general.query')}</a></li>").click -> openPrivateChannel user.userID
menu.append $ "<li><a>#{WCF.Language.get('chat.general.kick')}</a></li>"
menu.append $ "<li><a>#{WCF.Language.get('chat.general.ban')}</a></li>"
menu.append $ """<li><a href="#{user.link}">#{WCF.Language.get('chat.general.profile')}</a></li>"""
events.userMenu.fire user, menu
menu = $(v.userMenuTemplate.fetch user)
if menu.find('li').length
li.append menu
@ -560,7 +634,6 @@ Build HTML of the user and insert it into the list, if the users was not found i
li.addClass 'dropdown'
li.appendTo $ '#timsChatUserList > ul'
foundUsers[id] = true
Remove all users that left the chat.
@ -578,12 +651,17 @@ Insert the given `text` into the input. If `options.append` is true the given `t
the existing text. If `options.submit` is true the message will be sent to the server afterwards.
insertText = (text, options = { }) ->
options.append = false if options.prepend? and options.prepend and not options.append?
options = $.extend
prepend: false
append: true
submit: false
, options
text = text + $('#timsChatInput').val() if options.prepend
text = $('#timsChatInput').val() + text if options.append
$('#timsChatInput').val text
do $('#timsChatInput').keyup
@ -595,19 +673,21 @@ the existing text. If `options.submit` is true the message will be sent to the s
Send out notifications for the given `message`. The number of unread messages will be prepended to `document.title` and if available desktop notifications will be sent.
notify = (message) ->
return if message.sender is WCF.User.userID
if scrollUpNotifications
$('.timsChatMessageContainer.active').addClass 'notification'
$("#timsChatMessageTabMenu > .tabMenu > ul > li.ui-state-active").addClass 'notify'
$(".timsChatMessageContainer.active").addClass 'notify'
if message.isInPrivateChannel
if message.sender is WCF.User.userID
privateChannelID = message.receiver
else
privateChannelID = message.sender
id = if message.sender is WCF.User.userID then message.receiver else message.sender
if $('.timsChatMessageContainer.active').data('userID') isnt privateChannelID
$("#privateChannel#{privateChannelID}").addClass 'notify'
if $('.timsChatMessageContainer.active').data('userID') isnt id
$("#timsChatMessageTabMenuAnchor#{id}").parent().addClass 'notify'
$("#timsChatMessageContainer#{id}").addClass 'notify'
else if $('.timsChatMessageContainer.active').data('userID') isnt 0
$("#privateChannel0").addClass 'notify'
$("#timsChatMessageTabMenuAnchor0").parent().addClass 'notify'
$("#timsChatMessageContainer0").addClass 'notify'
return if isActive or $('#timsChatNotify').data('status') is 0
@ -645,8 +725,9 @@ Fetch the roomlist from the server and update it in the GUI.
for room in data.returnValues
li = $ '<li></li>'
li.addClass('timsChatRoom').data('roomID', room.roomID)
li.addClass 'active' if room.active
$("""<a href="#{room.link}">#{WCF.String.escapeHTML(room.title)} <span class="badge">#{WCF.String.formatNumeric room.userCount}</span></a>""").addClass('timsChatRoom').data('roomID', room.roomID).appendTo li
$("""<a href="#{room.link}">#{WCF.String.escapeHTML(room.title)}</a> <span class="badge">#{WCF.String.formatNumeric room.userCount}</span>""").appendTo li
$('#timsChatRoomList ul').append li
if window.history?.replaceState?
@ -702,7 +783,7 @@ Joins a room.
success: (data) ->
loading = false
$('#timsChatTopic').removeClass 'hidden'
$('#timsChatTopic').removeClass 'invisible'
currentRoom = data.returnValues
currentRoom.roomID = roomID
@ -734,66 +815,72 @@ Open private channel
div = $ '<div>'
div.attr 'id', "timsChatMessageContainer#{userID}"
div.data 'userID', userID
div.addClass 'tabMenuContent'
div.addClass 'timsChatMessageContainer'
div.addClass 'marginTop'
div.addClass 'container'
div.wrapInner '<ul>'
div.addClass 'containerPadding'
div.wrapInner "<ul></ul>"
div.on 'scroll', (event) ->
do event.stopPropagation
handleScroll event
$('#timsChatMessageContainer0').after div
$('.privateChannel').removeClass 'active'
if userID isnt 0
$('#timsChatTopic').removeClass 'hidden empty'
$('#timsChatTopic').removeClass 'empty'
$('#timsChatTopic > .topic').html WCF.Language.get 'chat.general.privateChannelTopic', {username: userList.allTime[userID].username}
$('#timsChatTopic > .jsTopicCloser').attr 'title', WCF.Language.get 'chat.general.closePrivateChannel'
$('#timsChatMessageTabMenu').removeClass 'singleTab'
unless $.wcfIsset "privateChannel#{userID}"
unless $.wcfIsset "timsChatMessageTabMenuAnchor#{userID}"
li = $ '<li>'
li.attr 'id', "privateChannel#{userID}"
li.data 'privateChannelID', userID
li.addClass 'privateChannel'
span = $ '<span class="userAvatar framed" />'
anchor = $ """<a id="timsChatMessageTabMenuAnchor#{userID}" class="timsChatMessageTabMenuAnchor" href="#{window.location.toString().replace /#.+$/, ''}#timsChatMessageContainer#{userID}" />"""
anchor.data 'userID', userID
avatar = $ userList.allTime[userID].avatar[16]
avatar.addClass 'jsTooltip'
avatar.attr 'title', userList.allTime[userID].username
avatar.wrap span
li.append avatar.parent().addClass 'small'
avatar = $('<span class="userAvatar framed" />').wrapInner avatar
avatar.append "<span>#{userList.allTime[userID].username}</span>"
avatar = $ userList.allTime[userID].avatar[32]
avatar.addClass 'jsTooltip'
avatar.attr 'title', userList.allTime[userID].username
avatar.wrap span
li.append avatar.parent().addClass 'large'
anchor.wrapInner avatar
anchor.prepend '<span class="icon icon16 icon-warning-sign notifyIcon"></span>'
anchor.append """<span class="jsChannelCloser icon icon16 icon-remove jsTooltip" title="#{WCF.Language.get('chat.global.closePrivateChannel')}" />"""
$('#privateChannelsMenu ul').append li
li.append anchor
$('#privateChannelsMenu').addClass 'shown'
$('#timsChatMessageTabMenu > .tabMenu > ul').append li
$('#timsChatMessageTabMenu').wcfTabs 'refresh'
WCF.System.FlexibleMenu.rebuild $('#timsChatMessageTabMenu > .tabMenu').attr 'id'
else
$('#timsChatTopic > .topic').text currentRoom.topic
$('#timsChatTopic > .jsTopicCloser').attr 'title', WCF.Language.get 'chat.general.closeTopic'
if currentRoom.topic.trim() is ''
$('#timsChatTopic').addClass 'empty'
else
$('#timsChatTopic').removeClass 'empty'
do WCF.DOMNodeInsertedHandler.execute
$('.timsChatMessageContainer').removeClass 'active'
$("#timsChatMessageContainer#{userID}").addClass 'active'
$("#privateChannel#{userID}").addClass('active').removeClass 'notify'
$("#timsChatMessageTabMenuAnchor#{userID}").parent().removeClass 'notify'
$("#timsChatMessageContainer#{userID}").removeClass 'notify'
if $('#timsChatAutoscroll').data('status')
do $('#timsChatAutoscroll').click
scrollUpNotifications = on
$('#timsChatMessageTabMenu').wcfTabs 'select', $("#timsChatMessageTabMenuAnchor#{userID}").parent().index()
do WCF.DOMNodeInsertedHandler.execute
openChannel = userID
Close private channel
closePrivateChannel = (userID) ->
unless userID is 0
do $("#privateChannel#{userID}").remove
do $("#timsChatMessageTabMenuAnchor#{userID}").parent().remove
do $("#timsChatMessageContainer#{userID}").remove
$('#timsChatMessageTabMenu').wcfTabs 'refresh'
WCF.System.FlexibleMenu.rebuild $('#timsChatMessageTabMenu > .tabMenu').wcfIdentify()
if $('#privateChannelsMenu li').length <= 1
$('#privateChannelsMenu').removeClass 'shown'
if $('#timsChatMessageTabMenu > .tabMenu > ul > li').length <= 1
$('#timsChatMessageTabMenu').addClass 'singleTab'
openPrivateChannel 0
@ -965,7 +1052,7 @@ Create a message containing the uploaded attachment
unless parseInt(data.returnValues.attachments[internalFileID].isImage) is 0
link.addClass('jsImageViewer')
if !data.returnValues.attachments[internalFileID].tinyURL
unless data.returnValues.attachments[internalFileID].tinyURL
li.find('.box32 > div.attachmentImageContainer > .icon-paper-clip').replaceWith $("""<img src="#{data.returnValues.attachments[internalFileID].url}'" alt="" class="attachmentTinyThumbnail" style="width: 32px; height: 32px;" />""")
li.find('.attachmentTinyThumbnail').wrap link

View File

@ -1,11 +1,42 @@
/**
* Styles for Tims Chat
* Styles for Tims Chat
*
* @author Tim Düsterhus, Maximilian Mader
* @copyright 2010-2013 Tim Düsterhus
* @copyright 2010-2014 Tim Düsterhus
* @license Creative Commons Attribution-NonCommercial-ShareAlike <http://creativecommons.org/licenses/by-nc-sa/3.0/legalcode>
* @package be.bastelstu.chat
*/
@-webkit-keyframes timsChatNotify {
from {
border-color: @wcfContainerBorderColor;
}
to {
border-color: @wcfInputHoverBorderColor;
}
}
@-moz-keyframes timsChatNotify {
from {
border-color: @wcfContainerBorderColor;
}
to {
border-color: @wcfInputHoverBorderColor;
}
}
@-o-keyframes timsChatNotify {
from {
border-color: @wcfContainerBorderColor;
}
to {
border-color: @wcfInputHoverBorderColor;
}
}
@keyframes timsChatNotify {
from {
border-color: @wcfContainerBorderColor;
@ -16,118 +47,96 @@
}
}
.__bubbleArrow {
border-color: transparent @wcfContainerBorderColor;
left: -6px;
top: 5px;
border-width: 6px 6px 6px 0;
border-style: solid;
content: "";
display: block;
position: absolute;
width: 0;
}
// only apply styles to Tims Chat
#tplChat {
#content {
// styles related to the topic container
#timsChatTopic {
padding: @wcfGapTiny;
.transition(height, .2s);
.transition(padding-top, .2s);
.transition(padding-bottom, .2s);
position: relative;
&.empty, &.hidden {
height: 0px;
overflow: hidden;
border: 0px;
padding: 0px;
margin: 0px;
}
.jsTopicCloser {
cursor: pointer;
float: right;
}
}
#privateChannelsMenu {
.transition(opacity, .2s);
.marginTop;
z-index: -1;
#timsChatTopicCloser {
position: absolute;
opacity: 0;
&.shown {
opacity: 1;
z-index: 130;
~ .timsChatMessageContainer {
margin-left: 35px;
border-top-left-radius: 0;
}
}
> ul {
text-align: right;
> li:first-child {
> .userAvatar.framed {
img, > canvas, > .icon {
border-radius: @wcfContainerBorderRadius 0 0 0;
}
}
}
> li:last-child {
> .userAvatar.framed {
img, > canvas, > .icon {
border-radius: 0 0 0 @wcfContainerBorderRadius;
}
}
}
> li {
margin-bottom: -1px;
background-color: @wcfContainerBackgroundColor;
> .userAvatar, .userAvatar > .icon {
top: @wcfGapSmall;
right: @wcfGapSmall;
cursor: pointer;
&:hover {
color: @wcfLinkColor;
}
}
}
> .userAvatar {
&.large {
#timsChatMessageTabMenu {
&.singleTab {
> nav.tabMenu {
// hide tab menu when single tabbed
// this is “a bit” hacky
display: none !important;
}
// overwrite WCF margin overwrite on tab menu content with WCF marginTop like margin
// this is “a bit” hacky
margin-top: @wcfGapMedium !important;
> .tabMenuContent {
}
.timsChatMessageContainer {
&.notify {
-webkit-animation-duration: .2s;
-webkit-animation-name: timsChatNotify;
-webkit-animation-iteration-count: 5;
-webkit-animation-direction: alternate;
-webkit-animation-timing-function: linear;
-moz-animation-duration: .2s;
-moz-animation-name: timsChatNotify;
-moz-animation-iteration-count: 5;
-moz-animation-direction: alternate;
-moz-animation-timing-function: linear;
-o-animation-duration: .2s;
-o-animation-name: timsChatNotify;
-o-animation-iteration-count: 5;
-o-animation-direction: alternate;
-o-animation-timing-function: linear;
animation-duration: .2s;
animation-name: timsChatNotify;
animation-iteration-count: 5;
animation-direction: alternate;
animation-timing-function: linear;
border-color: @wcfInputHoverBorderColor;
}
}
}
> nav.tabMenu {
> ul {
> li {
> a {
.notifyIcon {
display: none;
}
&.small {
display: block;
}
> .icon, img {
// fix vertical alignment of images in tabs
vertical-align: middle;
margin-right: @wcfGapTiny;
}
&.active {
> .userAvatar {
&.large {
display: block;
}
&.small {
display: none;
}
&.framed {
> img, > canvas, > .icon {
border-right-color: @wcfContentBackgroundColor;
border-radius: @wcfContainerBorderRadius 0 0 @wcfContainerBorderRadius;
}
// styles related to the close button of private channels in tabs
.jsChannelCloser {
&:hover {
color: @wcfLinkColor;
}
}
}
&.notify {
> .userAvatar {
> * {
// TODO
opacity: .4;
.notifyIcon {
display: inline;
}
}
}
@ -136,246 +145,303 @@
}
.timsChatMessageContainer {
height: 320px;
height: 300px;
overflow-y: scroll;
overflow-x: hidden;
.timsChatMarkContainer {
display: none;
&.active {
display: block;
}
&.markEnabled {
ul {
.timsChatMessage {
&.jsMarked {
background-color: @wcfSelectedBackgroundColor;
color: @wcfSelectedColor;
}
> .innerMessageContainer {
.markContainer {
display: block;
}
}
}
}
}
ul {
.timsChatMessage {
min-height: 20px;
> ul {
> .timsChatMessage {
clear: both;
.transition(opacity, .2s);
&.unloaded {
opacity: .5;
opacity: 0.5;
}
&:nth-child(even) {
> .innerMessageContainer.bubble .innerMessage {
background-color: @wcfContainerAccentBackgroundColor;
&:after {
border-color: transparent @wcfContainerAccentBackgroundColor !important;
}
}
}
.messageIcon {
> .timsChatMessageIcon {
float: left;
padding: 8px 0 0 4px;
margin-right: -16px;
}
> .innerMessageContainer {
padding: 5px 20px 5px 5px;
> .timsChatInnerMessageContainer {
position: relative;
> div.avatarContainer {
> .timsChatAvatarContainer {
position: relative;
float: left;
margin-left: 16px;
> .avatarExtra {
box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.3);
> .timsChatAvatarExtraIcon {
position: absolute;
left: 24px;
bottom: -2px;
width: 16px;
height: 16px;
bottom: -8px;
right: -8px;
}
}
.innerMessage {
margin-left: 46px;
padding: 2px 5px 5px;
.timsChatInnerMessage {
margin-left: 32 + @wcfGapMedium; // size of avatars + a small gap
padding-right: @wcfGapSmall;
.timsChatUsernameContainer {
// sender username
> span:first-child {
font-weight: bold;
}
}
time {
float: right;
}
.username {
font-weight: bold;
}
> .text {
img {
max-width: 100%;
height: auto;
width: auto;
}
}
}
&.bubble {
> div.avatarContainer {
margin-left: 0;
> .timsChatAvatarContainer {
margin-left: 0px;
}
.innerMessage {
border-width: 1px;
border-style: solid;
border-color: @wcfContainerBorderColor;
border-radius: @wcfContainerBorderRadius;
background-color: @wcfContainerBackgroundColor;
.timsChatInnerMessage {
position: relative;
> ul.text {
padding: @wcfGapSmall;
border-radius: @wcfContainerBorderRadius;
border-style: solid;
border-width: 1px;
border-color: @wcfContainerBorderColor;
> .timsChatText {
li {
.clearfix;
border-style: solid;
border-width: 0 0 1px 0;
border-color: @wcfContainerBorderColor;
padding: 3px 0 4px;
padding: (@wcfGapTiny - 1) 0 @wcfGapTiny;
&:last-child {
border-style: none;
padding: 3px 0 0 0;
padding: (@wcfGapTiny - 1) 0 0 0;
}
}
}
&:before {
.__bubbleArrow;
// :before and :after are used to create the little arrows on the “speech bubble containers”
&:before, &:after {
content: "";
display: block;
position: absolute;
width: 0;
border-style: solid;
}
&:before{
border-color: transparent @wcfContainerBorderColor;
left: -@wcfGapSmall;
top: @wcfGapSmall;
border-width: @wcfGapSmall @wcfGapSmall @wcfGapSmall 0;
}
&:after {
.__bubbleArrow;
border-color: transparent @wcfContainerBackgroundColor;
left: -5px;
top: 6px;
border-width: 5px 5px 5px 0;
left: -@wcfGapSmall + 1;
top: @wcfGapSmall + 1;
border-width: (@wcfGapSmall - 1) (@wcfGapSmall - 1) (@wcfGapSmall - 1) 0;
}
}
&.right {
.avatarContainer {
> .timsChatAvatarContainer {
float: right;
}
.innerMessage {
margin-right: 46px;
margin-left: 0px;
.timsChatInnerMessage {
margin-right: 32 + @wcfGapMedium;
margin-left: 0;
&:before {
.__bubbleArrow;
&:before{
border-color: transparent @wcfContainerBorderColor;
left: auto;
right: -6px;
border-width: 6px 0 6px 6px;
right: -@wcfGapSmall;
top: @wcfGapSmall;
border-width: @wcfGapSmall 0 @wcfGapSmall @wcfGapSmall;
}
&:after {
.__bubbleArrow;
border-color: transparent @wcfContainerBackgroundColor;
left: auto;
right: -5px;
top: 6px;
border-width: 5px 0 5px 5px;
right: -@wcfGapSmall + 1;
top: @wcfGapSmall + 1;
border-width: (@wcfGapSmall - 1) 0 (@wcfGapSmall - 1) (@wcfGapSmall - 1);
}
}
}
}
}
> .markContainer {
display: none;
&:nth-child(even) {
> .timsChatInnerMessageContainer {
&.bubble {
.timsChatInnerMessage {
background-color: @wcfContainerAccentBackgroundColor;
&:after {
border-color: transparent @wcfContainerAccentBackgroundColor;
}
}
}
}
}
&:not(:last-child) {
> .timsChatInnerMessageContainer {
margin-bottom: @wcfGapSmall;
}
}
}
}
&.markEnabled {
> ul {
> .timsChatMessage {
&.jsMarked {
> .timsChatInnerMessageContainer {
.timsChatInnerMessage {
background-color: @wcfSelectedBackgroundColor;
color: @wcfSelectedColor;
}
&.bubble {
.timsChatInnerMessage {
&:after {
border-color: transparent @wcfSelectedBackgroundColor;
}
}
&.right {
.timsChatInnerMessage {
&:after {
border-color: transparent @wcfSelectedBackgroundColor;
}
}
}
}
}
}
> .timsChatInnerMessageContainer {
.timsChatMarkContainer {
display: inline-block;
position: absolute;
right: 0px;
top: 6px;
right: -@wcfGapLarge;
top: 0;
}
}
}
}
}
}
#smilies {
.marginTop();
// don't display the smiley box on low resolution devices as a button will be shown instead
@media only screen and (max-width: 800px) {
display: none;
}
&, &.invisible {
-webkit-transition: opacity .2s ease-in-out;
-moz-transition: opacity .2s ease-in-out;
-ms-transition: opacity .2s ease-in-out;
-o-transition: opacity .2s ease-in-out;
transition: opacity .2s ease-in-out;
}
&.invisible {
opacity: .5;
}
}
#timsChatOptions {
float: right;
// styles related to the smiley button that replaces the smiley box on low resolution devices
#timsChatSmileyPopupButton {
display: none;
@media only screen and (max-width: 800px) {
display: inline-block;
}
}
// fix option buttons on low resolution devices, it will become a dropup not a dropdown
> nav.buttonGroupNavigation {
position: relative;
display: inline-block;
vertical-align: middle;
@media only screen and (max-width: 800px) {
margin-right: @wcfGapSmall;
> ul {
right: 1px;
top: auto;
bottom: 24px;
}
}
}
// clear the float on the first element after the option container, maybe someone alters their templates and puts an element down there ;)
+ * {
clear: right;
}
}
}
.sidebar {
padding-top: 0px !important;
> div {
> fieldset{
padding-right: @wcfGapTiny;
> div {
height: 400px;
overflow: auto !important;
> .chatTabMenuContainer {
padding: 14px 0 21px;
overflow-y: auto;
> .chatSidebarMenu {
background: @wcfContentBackgroundColor;
margin: -14px 0 0;
border-radius: 0px;
}
> ul {
padding-right: @wcfGapSmall;
}
}
#sidebarContent {
fieldset {
padding-top: 0px;
padding-bottom: 0px;
}
nav {
ul {
> li {
> a {
padding: @wcfGapTiny @wcfGapMedium @wcfGapTiny @wcfGapLarge;
height: 24px; // height of avatar image
}
> a:before {
display: inline-block;
content: "";
height: 100%;
vertical-align: middle;
}
> &.active {
margin-top: @wcfGapSmall;
}
}
&#timsChatUserListContainer {
> div {
height: 250px;
}
}
ul:not(.dropdownMenu) {
> li {
margin-top: @wcfGapSmall;
&#timsChatRoomListContainer {
> div#timsChatRoomList {
height: 150px;
}
}
}
}
#timsChatUserList {
.timsChatUser {
> a {
background: @wcfContentBackgroundColor;
> ul {
> li {
&.you {
a {
&:hover {
text-decoration: none;
cursor: default;
}
}
}
a {
img {
margin-right: @wcfGapSmall;
margin-right: @wcfGapTiny;
}
}
&.away {
opacity: .5;
}
&.suspended a {
text-decoration: line-through;
}
}
}
}
@ -391,116 +457,35 @@
}
}
#timsChatRoomList {
#timsChatCopyrightDialog {
> div {
> div {
text-align: center;
}
}
}
#smilies {
margin-top: @wcfGapMedium;
li {
.transition(opacity, .2s);
}
&.disabled {
li {
opacity: .5;
}
}
}
#timsChatOptions {
> ul {
text-align: right;
}
}
#timsChatCopyrightDialog > div {
background-position: right center;
background-repeat: no-repeat;
min-height: 150px;
background-position: right top;
}
}
.notification {
animation-duration: .2s;
animation-name: timsChatNotify;
animation-iteration-count: 5;
animation-direction: alternate;
animation-timing-function: linear;
border-color: @wcfInputHoverBorderColor;
}
@media only screen and (max-width: 800px) {
.timsChatMessage .text li > time, #smilies {
display: none !important;
}
#fish {
font-size: 2rem;
}
}
// TODO
html.fullscreen {
#top {
height: 0px;
}
&,
body,
#tplChat #main,
#tplChat #main > div,
#tplChat #main > div > div,
#tplChat #content {
width: 100%;
height: 100%;
overflow: hidden;
#content {
height: 100%;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
display: -moz-box;
display: -webkit-box;
display: -webkit-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
-webkit-box-direction: normal;
-moz-box-direction: normal;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
.timsChatMessageContainer {
-webkit-box-flex: 1;
-moz-box-flex: 1;
-webkit-flex: 1 0 auto;
-ms-flex: 1 0 auto;
flex: 1 0 auto;
}
max-width: 100%;
max-height: 100%;
}
#timsChatOptions {
margin-bottom: @wcfGapMedium;
}
#tplChat {
height: 100%;
overflow: hidden;
#pageHeader, #pageFooter {
#top,
#pageHeader,
#pageFooter {
display: none;
}
#main {
height: 100%;
margin: 0;
padding: 0;
max-width: 100%;
width: 100%;
> div, .sidebar, #sidebarContainer {
height: 100%;
}
}
}
}

View File

@ -36,10 +36,10 @@
<dd>
<ul>
<li><a href="http://www.wbbaddons.de/user/2020-noone/" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>-noone-</a></li>
<li><a href="https://github.com/Gabbid" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>Gabi</a></li>
<li>Gabi</li>
<li>Alexandra Glass</li>
<li><a href="https://github.com/Leon-" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK} target="_blank"{/if}>Stefan Hahn</a></li>
<li><a href="https://kittblog.com/" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>Matthias Kittsteiner</a></li>
<li><a href="http://kittmedia.com/" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>Matthias Kittsteiner</a></li>
<li><a href="http://www.wbbaddons.de" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>Martin Schwendowius</a></li>
<li><a href="http://www.cls-design.com/" class="externalURL" {if EXTERNAL_LINK_TARGET_BLANK}target="_blank"{/if}>Tom</a></li>
</ul>

View File

@ -10,39 +10,44 @@
(function ($, window) {
$(function(){
WCF.Language.addObject({
'chat.general.query': '{lang}chat.general.query{/lang}',
'chat.general.kick': '{lang}chat.general.kick{/lang}',
'chat.general.ban': '{lang}chat.general.ban{/lang}',
'chat.general.profile': '{lang}chat.general.profile{/lang}',
'chat.general.notify.title': '{lang}chat.general.notify.title{/lang}',
'chat.general.privateChannelTopic': '{lang}chat.general.privateChannelTopic{/lang}',
'chat.general.closePrivateChannel': '{lang}chat.general.closePrivateChannel{/lang}',
'chat.general.closeTopic': '{lang}chat.general.closeTopic{/lang}',
'chat.error.onMessageLoad': '{@"chat.error.onMessageLoad"|language|encodeJS}',
'chat.general.notify.title': '{lang}chat.general.notify.title{/lang}',
'chat.general.privateChannelTopic': '{lang}chat.general.privateChannelTopic{/lang}',
'chat.general.profile': '{lang}chat.general.profile{/lang}',
'chat.general.query': '{lang}chat.general.query{/lang}',
'chat.general.whisper': '{lang}chat.general.whisper{/lang}',
'chat.error.duplicateTab': '{lang}chat.error.duplicateTab{/lang}',
'chat.error.join': '{lang}chat.error.join{/lang}',
'chat.error.onMessageLoad': '{@"chat.error.onMessageLoad"|language|encodeJS}',
'chat.error.reload': '{lang}chat.error.reload{/lang}',
'chat.message.{$messageTypes[TYPE_ATTACHMENT]}': '{lang}chat.message.{$messageTypes[TYPE_ATTACHMENT]}{/lang}',
'wcf.attachment.insert': '{lang}wcf.attachment.insert{/lang}',
'wcf.attachment.delete.sure': '{lang}wcf.attachment.delete.sure{/lang}',
'wcf.attachment.upload.error.invalidExtension': '{lang}wcf.attachment.upload.error.invalidExtension{/lang}',
'wcf.attachment.upload.error.tooLarge': '{lang}wcf.attachment.upload.error.tooLarge{/lang}',
'wcf.attachment.upload.error.reachedLimit': '{lang}wcf.attachment.upload.error.reachedLimit{/lang}',
'wcf.attachment.upload.error.reachedRemainingLimit': '{lang}wcf.attachment.upload.error.reachedRemainingLimit{/lang}',
'wcf.attachment.upload.error.uploadFailed': '{lang}wcf.attachment.upload.error.uploadFailed{/lang}',
'wcf.global.button.upload': '{lang}wcf.global.button.upload{/lang}',
'wcf.attachment.insert': '{lang}wcf.attachment.insert{/lang}',
'wcf.attachment.delete.sure': '{lang}wcf.attachment.delete.sure{/lang}',
'chat.message.{$messageTypes[TYPE_ATTACHMENT]}': '{lang}chat.message.{$messageTypes[TYPE_ATTACHMENT]}{/lang}'
'wcf.global.button.upload': '{lang}wcf.global.button.upload{/lang}'
});
// Boot the chat
{if MODULE_SMILEY}WCF.TabMenu.init();{/if}
{if MODULE_ATTACHMENT && $__wcf->session->getPermission('user.chat.canUploadAttachment')}
new be.bastelstu.Chat.Attachment();
new be.bastelstu.Chat.Action.Delete('wcf\\data\\attachment\\AttachmentAction', '#timsChatUploadDropdownMenu > li');
{/if}
WCF.TabMenu.init();
{if MODULE_SMILEY}
new WCF.Message.Smilies();
{/if}
{capture assign='messageTemplate'}{include application='chat' file='message'}{/capture}
{capture assign='userTemplate'}{include application='chat' file='userListUser'}{/capture}
{capture assign='userMenuTemplate'}{include application='chat' file='userListUserMenu'}{/capture}
var config = {
reloadTime: {@CHAT_RELOADTIME},
@ -58,12 +63,13 @@
config,
new WCF.Template('{literal}{if $newMessageCount}({#$newMessageCount}) {/if}{$title} - {/literal}{"chat.general.title"|language|encodeJS} - {PAGE_TITLE|language|encodeJS}'),
new WCF.Template('{@$messageTemplate|encodeJS}'),
new WCF.Template('{@$userTemplate|encodeJS}')
new WCF.Template('{@$userTemplate|encodeJS}'),
new WCF.Template('{@$userMenuTemplate|encodeJS}')
);
{event name='afterInit'}
$('#timsChatCopyright').click(function (event) {
$('#timsChatCopyright a').click(function (event) {
event.preventDefault();
if (!$.wcfIsset('timsChatCopyrightDialog')) $('<div id="timsChatCopyrightDialog"></div>').appendTo('body');
$('#timsChatCopyrightDialog').load('{link application="chat" controller="Copyright"}{/link}').wcfDialog({
@ -80,28 +86,31 @@
{capture assign='sidebar'}{include application='chat' file='sidebar'}{/capture}
{include file='header' sandbox=false sidebarOrientation='right'}
<div id="timsChatTopic" class="marginTop container{if $room->topic|language === ''} empty{/if}">
<span class="icon icon16 icon-remove jsTopicCloser jsTooltip" title="{lang}chat.general.closeTopic{/lang}"></span>
<div class="clearfix">
<div id="timsChatTopic" class="container containerPadding marginTop{if $room->topic|language === ''} empty{/if}">
<span id="timsChatTopicCloser" class="icon icon16 icon-remove jsTooltip" title="{lang}chat.general.closeTopic{/lang}"></span>
<span class="topic">{$room->topic|language}</span>
</div>
<div id="privateChannelsMenu">
<div id="timsChatMessageTabMenu" class="tabMenuContainer singleTab" data-active="timsChatMessageContainer0">
<nav class="tabMenu">
<ul>
<li id="privateChannel0" class="privateChannel active" data-private-channel-id="0">
<span class="userAvatar framed small">
<span class="icon icon16 icon-comment-alt jsTooltip" title="{lang}chat.general.room{/lang}"></span>
</span>
<span class="userAvatar framed large">
<span class="icon icon32 icon-comment-alt jsTooltip" title="{lang}chat.general.room{/lang}"></span>
</span>
<li>
<a id="timsChatMessageTabMenuAnchor0" href="{$__wcf->getAnchor('timsChatMessageContainer0')}" class="timsChatMessageTabMenuAnchor" data-user-id="0">
<span class="icon icon16 icon-warning-sign notifyIcon"></span>{*
*}<span class="userAvatar framed">
<span class="icon icon16 icon-group"></span>
</span>{*
*}<span>{$room}</span>
</a>
</li>
</ul>
</div>
</nav>
<div id="timsChatMessageContainer0" class="timsChatMessageContainer marginTop container active" data-user-id="0">
<div id="timsChatMessageContainer0" class="tabMenuContent timsChatMessageContainer container containerPadding active" data-user-id="0">
<p class="error noJsOnly" style="display: none;">{lang}chat.general.noJs{/lang}</p>
<ul>
</ul>
<ul></ul>
</div>
</div>
<form id="timsChatForm" action="{link application='chat' controller='Chat' action='Send'}{/link}" method="post">
@ -116,42 +125,79 @@
</dd>
</dl>
</fieldset>
<button type="submit" class="invisible" accesskey="s"></button>
<button type="submit" class="marginTop invisible" accesskey="s">{lang}wcf.global.button.submit{/lang}</button>
</form>
{if MODULE_SMILEY && $smileyCategories|count}
{include file='messageFormSmilies' wysiwygSelector=''}
{/if}
<nav id="timsChatOptions" class="marginTop jsMobileNavigation buttonGroupNavigation">
<span class="invisible">{lang}chat.general.controls{/lang}</span>
<ul class="smallButtons buttonGroup">
<li><a id="timsChatAutoscroll" accesskey="d" class="button active timsChatToggle jsTooltip" title="{lang}chat.general.scroll{/lang}" data-status="1"><span class="icon icon16 icon-arrow-down"></span><span class="invisible">{lang}chat.general.scroll{/lang}</span></a></li>{*
<div id="timsChatOptions" class="marginTop">
<span id="timsChatSmileyPopupButton" class="button smallButtons">
<span class="icon icon16 icon-smile"></span>
<span>{lang}chat.general.smilies{/lang}</span>
</span>
*}<li><a id="timsChatFullscreen" accesskey="f" class="button timsChatToggle jsTooltip" title="{lang}chat.general.fullscreen{/lang}" data-status="0"><span class="icon icon16 icon-fullscreen"></span><span class="invisible">{lang}chat.general.fullscreen{/lang}</span></a></li>{*
<nav class="jsMobileNavigation buttonGroupNavigation">
<ul class="buttonGroup">
<li>
<a id="timsChatAutoscroll" accesskey="d" class="button active timsChatToggle jsTooltip" title="{lang}chat.general.scroll{/lang}" data-status="1">
<span class="icon icon16 icon-arrow-down"></span>
<span class="invisible">{lang}chat.general.scroll{/lang}</span>
</a>
</li>
<li>
<a id="timsChatFullscreen" accesskey="f" class="button timsChatToggle jsTooltip" title="{lang}chat.general.fullscreen{/lang}" data-status="0">
<span class="icon icon16 icon-fullscreen"></span>
<span class="invisible">{lang}chat.general.fullscreen{/lang}</span>
</a>
</li>
<li>
<a id="timsChatNotify" accesskey="n" class="button timsChatToggle jsTooltip" title="{lang}chat.general.notify{/lang}" data-status="0">
<span class="icon icon16 icon-bell-alt"></span>
<span class="invisible">{lang}chat.general.notify{/lang}</span>
</a>
</li>
*}<li><a id="timsChatNotify" accesskey="n" class="button timsChatToggle jsTooltip" title="{lang}chat.general.notify{/lang}" data-status="0"><span class="icon icon16 icon-bell-alt"></span><span class="invisible">{lang}chat.general.notify{/lang}</span></a></li>{*
{if MODULE_SMILEY && $smileyCategories|count}
<li>
<a id="timsChatSmilies" accesskey="e" class="button{if ENABLE_SMILIES_DEFAULT_VALUE} active{/if} timsChatToggle jsTooltip" title="{lang}chat.general.smilies{/lang}" data-status="{@ENABLE_SMILIES_DEFAULT_VALUE}">
<span class="icon icon16 icon-smile"></span>
<span class="invisible">{lang}chat.general.smilies{/lang}</span>
</a>
</li>
{/if}
*}<li{if !MODULE_SMILEY || !$smileyCategories|count} style="display: none;"{/if}><a id="timsChatSmilies" accesskey="e" class="button{if ENABLE_SMILIES_DEFAULT_VALUE} active{/if} timsChatToggle jsTooltip" title="{lang}chat.general.smilies{/lang}" data-status="{@ENABLE_SMILIES_DEFAULT_VALUE}"><span class="icon icon16 icon-smile"></span><span class="invisible">{lang}chat.general.smilies{/lang}</span></a></li>{*
*}{if MODULE_ATTACHMENT && $__wcf->session->getPermission('user.chat.canUploadAttachment')}{*
*}<li id="timsChatUploadContainer" class="dropdown" data-max-size="{$attachmentHandler->getMaxSize()}">
{if MODULE_ATTACHMENT && $__wcf->session->getPermission('user.chat.canUploadAttachment')}
<li id="timsChatUploadContainer" class="dropdown" data-max-size="{$attachmentHandler->getMaxSize()}">
<a id="timsChatUpload" class="dropdownToggle button jsTooltip" title="{lang}wcf.attachment.attachments{/lang}" data-toggle="timsChatUploadContainer">
<span class="icon icon16 icon-paper-clip"></span>
<span class="invisible">{lang}wcf.attachment.attachments{/lang}</span>
</a>
<ul id="timsChatUploadDropdownMenu" class="dropdownMenu">
<li class="uploadButton" style="margin-top: 0;">
<span><label for="timsChatUploadInput">{lang}wcf.global.button.upload{/lang}</label></span>
<span><label for="timsChatUploadInput" class="pointer">{lang}wcf.global.button.upload{/lang}</label></span>
</li>
</ul>
</li>{/if}{*
</li>
{/if}
*}<li><a id="timsChatClear" class="button jsTooltip" title="{lang}chat.general.clear{/lang}"><span class="icon icon16 icon-remove"></span><span class="invisible">{lang}chat.general.clear{/lang}</span></a></li>{*
*}<li><a id="timsChatMark" class="button timsChatToggle jsTooltip" title="{lang}chat.general.mark{/lang}" data-status="0"><span class="icon icon16 icon-check"></span><span class="invisible">{lang}chat.general.mark{/lang}</span></a></li>
<li>
<a id="timsChatClear" class="button jsTooltip" title="{lang}chat.general.clear{/lang}">
<span class="icon icon16 icon-remove"></span>
<span class="invisible">{lang}chat.general.clear{/lang}</span>
</a>
</li>
<li>
<a id="timsChatMark" class="button timsChatToggle jsTooltip" title="{lang}chat.general.mark{/lang}" data-status="0">
<span class="icon icon16 icon-check"></span>
<span class="invisible">{lang}chat.general.mark{/lang}</span>
</a>
</li>
</ul>
</nav>
</div>
</div>
{include file='footer' sandbox=false}
</body>

View File

@ -1,12 +1,12 @@
{literal}
{if $message.type == $messageTypes.LEAVE || $message.type == $messageTypes.JOIN}
<div class="messageIcon">
<span class="icon icon16 icon-{if $message.type == $messageTypes.LEAVE}signout{elseif $message.type == $messageTypes.JOIN}signin{/if}"></span>
{if $message.type == $messageTypes.JOIN || $message.type == $messageTypes.LEAVE}
<div class="timsChatMessageIcon">
<span class="icon icon16 icon-{if $message.type == $messageTypes.JOIN}signin{else}signout{/if}"></span>
</div>
{/if}
<div class="innerMessageContainer{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER || $message.type == $messageTypes.INFORMATION || $message.type == $messageTypes.ATTACHMENT} bubble{/if}{if $message.type == $messageTypes.WHISPER && $message.sender != $__wcf.User.userID} right{/if}">
<div class="avatarContainer">
<div class="userAvatar{if $message.type != $messageTypes.INFORMATION} framed{/if}">
<div class="timsChatInnerMessageContainer{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER || $message.type == $messageTypes.INFORMATION || $message.type == $messageTypes.ATTACHMENT} bubble{/if}{if $message.type == $messageTypes.WHISPER && $message.sender != $__wcf.User.userID} right{/if}">
<div class="timsChatAvatarContainer">
<div class="userAvatar framed">
{if $message.type != $messageTypes.INFORMATION}
{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER || $message.type == $messageTypes.ATTACHMENT}
{@$message.avatar[32]}
@ -18,26 +18,18 @@
{/if}
</div>
{if $message.type == $messageTypes.ATTACHMENT}
<small class="framed avatarExtra">
<small class="framed timsChatAvatarExtraIcon">
<span class="icon icon16 icon-paperclip"></span>
</small>
{/if}
</div>
<div class="innerMessage">
<span class="username">
{if ($message.type == $messageTypes.WHISPER && $message.sender == WCF.User.userID) || $message.type != $messageTypes.WHISPER}
<div class="timsChatInnerMessage">
<span class="timsChatUsernameContainer">
{@$message.formattedUsername}
{else}
{$message.additionalData.receiverUsername}
{/if}
{if $message.type == $messageTypes.WHISPER}
<span class="icon icon16 icon-double-angle-{if $message.sender == WCF.User.userID}right{else}left{/if} jsTooltip" title="{/literal}{lang}chat.ui.whispers{/lang}{literal}" onclick="be.bastelstu.Chat.insertText('/whisper {if $message.receiver == WCF.User.userID}{$message.username.replace("\\", "\\\\").replace("'", "\\'")}{else}{$message.additionalData.receiverUsername.replace("\\", "\\\\").replace("'", "\\'")}{/if}, ', { append: false });"></span>
{if ($message.type == $messageTypes.WHISPER && $message.sender == WCF.User.userID) || $message.type != $messageTypes.WHISPER}
{$message.additionalData.receiverUsername}
{else}
{@$message.formattedUsername}
{/if}
<span class="icon icon16 icon-double-angle-right jsTooltip" title="{/literal}{lang}chat.general.whisper{/lang}{literal}" onclick="be.bastelstu.Chat.insertText('/whisper {if $message.receiver == WCF.User.userID}{$message.username.replace("\\", "\\\\").replace("'", "\\'")}{else}{$message.additionalData.receiverUsername.replace("\\", "\\\\").replace("'", "\\'")}{/if}, ', { append: false });"></span>
<span class="reciever">{$message.additionalData.receiverUsername}</span>
{/if}
</span>
@ -45,19 +37,19 @@
{if $message.type == $messageTypes.NORMAL || $message.type == $messageTypes.WHISPER || $message.type == $messageTypes.ATTACHMENT}
{if $message.type == $messageTypes.ATTACHMENT}<span>{lang}chat.message.{$messageTypes.ATTACHMENT}{/lang}</span>{/if}
<ul class="text">
<ul class="timsChatText">
<li>
{if $message.isFollowUp} <time>{@$message.formattedTime}</time>{/if}
{@$message.formattedMessage}
</li>
</ul>
{elseif $message.type == $messageTypes.INFORMATION}
<div class="text">{@$message.formattedMessage}</div>
<div class="timsChatText">{@$message.formattedMessage}</div>
{else}
<span class="text">{@$message.formattedMessage}</span>
<span class="timsChatText">{@$message.formattedMessage}</span>
{/if}
</div>
<span class="markContainer">
<span class="timsChatMarkContainer">
<input type="checkbox" value="{@$message.messageID}" />
</span>
</div>

View File

@ -1,29 +1,19 @@
<div class="tabMenuContainer chatTabMenuContainer containerPadding">
<nav class="menu chatSidebarMenu">
<ul>
<li id="toggleUsers" class="ui-state-active"><a href="{@$__wcf->getAnchor('timsChatUserList')}" title="{lang}chat.general.users{/lang}">{lang}chat.general.users{/lang} <span class="badge">0</span></a></li>
<li id="toggleRooms"><a href="{@$__wcf->getAnchor('timsChatRoomList')}" title="{lang}chat.general.rooms{/lang}">{lang}chat.general.rooms{/lang} <span class="badge">0</span></a></li>
</ul>
</nav>
<fieldset id="timsChatUserListContainer">
<legend>{lang}chat.general.users{/lang}</legend>
<div id="timsChatUserList">
<ul class="sidebarNestedCategoryList">
<section id="sidebarContent" class="tabMenuContent">
<fieldset>
<nav id="timsChatUserList">
<ul>
</ul>
</nav>
</fieldset>
</div>
</fieldset>
<fieldset id="timsChatRoomListContainer">
<legend>{lang}chat.general.rooms{/lang}</legend>
<div id="timsChatRoomList">
<ul class="sidebarNestedCategoryList">
<fieldset>
<nav id="timsChatRoomList" style="display: none;">
<div>
<ul>
</ul>
</div>
<div class="marginTop">
<button type="button">{lang}chat.general.forceRefresh{/lang}</button>
<a id="timsChatRoomListReloadButton" class="button small jsOnly">{lang}chat.general.forceRefresh{/lang}</a>
</div>
</div>
</nav>
</fieldset>
</section>
</div>
</fieldset>

View File

@ -1 +1 @@
{literal}<a{if $userID != $__wcf.User.userID} class="dropdownToggle"{/if} data-user-id="{$userID.toString()}">{@$avatar['24']}{$username}</a>{/literal}
{literal}<a{if $userID != $__wcf.User.userID} class="dropdownToggle"{/if} data-user-id="{$userID.toString()}"><span class="framed">{@$avatar['24']}</span>{$username}</a>{/literal}

View File

@ -0,0 +1,8 @@
{literal}
<ul data-user-id="{$userID}">
<li><a class="jsTimsChatUserMenuWhisper">{lang}chat.general.whisper{/lang}</a></li>
<li><a class="jsTimsChatUserMenuQuery">{lang}chat.general.query{/lang}</a></li>
<li><a class="jsTimsChatUserMenuBan">{lang}chat.general.ban{/lang}</a></li>
<li><a href="{$link}" class="userLink" data-user-id="{$userID}">{lang}chat.general.profile{/lang}</a></li>
</ul>
{/literal}