Initial xentral_oss_20.3.c9ffacf
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
{extends file='layout.tpl'}
|
||||
{namespace key='chat'}
|
||||
|
||||
{block name='page'}
|
||||
<div id="tabs">
|
||||
<ul>
|
||||
<li><a href="#tabs-1">{translate key='overview'}{$tabText|default:'Übersicht'}{/translate}</a></li>
|
||||
</ul>
|
||||
<div id="tabs-1" class="{$chatStandaloneClass}">
|
||||
<div id="chat-wrapper">
|
||||
<div id="sidebar-wrapper">
|
||||
<div id="sidebar-scroller">
|
||||
<div>
|
||||
<h3>{translate key='users'}Benutzer{/translate}</h3>
|
||||
<div id="userlist"></div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>{translate key='rooms'}Räume{/translate}</h3>
|
||||
<div id="roomlist"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="message-wrapper">
|
||||
<fieldset class="white">
|
||||
<legend id="message-header"></legend>
|
||||
<div id="message-scroller">
|
||||
<div id="message-area"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
<form id="chatform">
|
||||
<fieldset class="white">
|
||||
<legend></legend>
|
||||
<div class="input-wrapper">
|
||||
<div class="input-message">
|
||||
<input type="text" placeholder="{translate key='message_placeholder'}Nachricht{/translate}"
|
||||
name="nachricht" id="nachricht" autocomplete="off" tabindex="1">
|
||||
</div>
|
||||
<div class="input-prio">
|
||||
<label>
|
||||
<input type="checkbox" name="prio" id="prio" value="1" tabindex="2">
|
||||
{translate key='priority_message'}Prio-Nachricht{/translate}
|
||||
</label>
|
||||
</div>
|
||||
{block name='module_chat_list_form_button'}
|
||||
<div class="input-button">
|
||||
<input type="submit" name="submit" class="btnGreen"
|
||||
value="{translate key='submit_button_text'}absenden{/translate}" tabindex="3">
|
||||
</div>
|
||||
{/block}
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/block}
|
||||
@@ -0,0 +1,286 @@
|
||||
body > #tabs {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#page_container #tabs-1 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#tabs-1.chat-standalone {
|
||||
padding: 0;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
/**
|
||||
PUSH NOTIFICATIONS
|
||||
*/
|
||||
|
||||
#notification-info {
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
#tabs-1 #notification-info {
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
#notification-button {
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
/**
|
||||
LAYOUT
|
||||
*/
|
||||
|
||||
#chat-wrapper {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
font-size: 15px;
|
||||
line-height: 1.1em;
|
||||
}
|
||||
|
||||
#sidebar-wrapper {
|
||||
flex-direction: column;
|
||||
flex-basis: 220px;
|
||||
max-width: 240px;
|
||||
background-color: #ECECEC;
|
||||
padding: 10px;
|
||||
}
|
||||
#sidebar-wrapper h3 {
|
||||
color: var(--grey);
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
margin-top: 10px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#sidebar-scroller {
|
||||
overflow-y: auto;
|
||||
min-height: 100px;
|
||||
/*overflow: auto;*/
|
||||
}
|
||||
#sidebar-scroller > div {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#message-wrapper {
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
#message-wrapper fieldset {
|
||||
margin: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
#message-scroller {
|
||||
min-width: 270px;
|
||||
min-height: 180px;
|
||||
overflow-y: scroll;
|
||||
border-top: 3px solid var(--body-background);
|
||||
border-bottom: 3px solid var(--body-background);
|
||||
}
|
||||
|
||||
/**
|
||||
SIDEBAR
|
||||
*/
|
||||
|
||||
#roomlist,
|
||||
#userlist {
|
||||
}
|
||||
#roomlist ul,
|
||||
#userlist ul {
|
||||
width: 100%;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
#roomlist li,
|
||||
#userlist li {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
list-style-type: none;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
#roomlist li.active,
|
||||
#userlist li.active {
|
||||
padding: 6px;
|
||||
background-color: #FFF;
|
||||
}
|
||||
#roomlist li:before,
|
||||
#userlist li:before {
|
||||
content: '\25CB';
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
font-size: 170%;
|
||||
line-height: 15px;
|
||||
vertical-align: top;
|
||||
text-align: center;
|
||||
}
|
||||
#userlist li.online {
|
||||
color: var(--grey);
|
||||
}
|
||||
#userlist li.online:before {
|
||||
content: '\2022';
|
||||
font-size: 2.8em;
|
||||
color: #a9ca45;
|
||||
}
|
||||
#roomlist span,
|
||||
#userlist span {
|
||||
display: inline-block;
|
||||
}
|
||||
#roomlist .unread,
|
||||
#userlist .unread {
|
||||
font-weight: bold;
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
/**
|
||||
MESSAGE AREA
|
||||
*/
|
||||
#message-area .date {
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
height: 0;
|
||||
border-bottom: 1px solid #ECECEC;
|
||||
margin: 20px 0;
|
||||
text-align: center;
|
||||
}
|
||||
#message-area .date span {
|
||||
position: absolute;
|
||||
top: -8px;
|
||||
display: inline-block;
|
||||
background-color: #FFF;
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/**
|
||||
MESSAGE AREA HEADER
|
||||
*/
|
||||
|
||||
#message-header {
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
margin-top: 0;
|
||||
background-color: #ECECEC;
|
||||
}
|
||||
#message-header.public {
|
||||
color: orangered;
|
||||
}
|
||||
#message-header img {
|
||||
float: left;
|
||||
width: auto;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
}
|
||||
#message-header span {
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/**
|
||||
SINGLE MESSAGE
|
||||
*/
|
||||
|
||||
.message {
|
||||
box-sizing: content-box;
|
||||
min-height: 36px;
|
||||
padding: 0;
|
||||
margin: 9px;
|
||||
background-color: #FFF;
|
||||
-webkit-transition: background-color 5000ms linear;
|
||||
-ms-transition: background-color 5000ms linear;
|
||||
transition: background-color 5000ms linear;
|
||||
}
|
||||
.message:hover {
|
||||
}
|
||||
.message .image {
|
||||
float: left;
|
||||
min-height: 36px;
|
||||
margin-right: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.message .image img {
|
||||
width: auto;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
}
|
||||
.message.unread .text:after {
|
||||
content: ' (ungelesen)';
|
||||
display: inline;
|
||||
font-size: 80%;
|
||||
color: #999;
|
||||
}
|
||||
.message.read .text:after {
|
||||
}
|
||||
.message.new {
|
||||
background-color: #dfffff;
|
||||
}
|
||||
.message.new .time:after {
|
||||
content: ' NEU';
|
||||
display: inline;
|
||||
color: var(--tabs-background);
|
||||
font-weight: bold;
|
||||
}
|
||||
.message.prio {
|
||||
background-image: url('../themes/new/images/chat_prio.png');
|
||||
background-position: 99% center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.message .head {
|
||||
padding-left: 44px;
|
||||
}
|
||||
.message .time {
|
||||
color: #999;
|
||||
font-size: 80%;
|
||||
margin: 0 5px;
|
||||
}
|
||||
.message .sender {
|
||||
font-weight: bold;
|
||||
}
|
||||
.message .text {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
word-wrap: break-word;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
padding: 0 44px;
|
||||
line-height: 1.4em;
|
||||
}
|
||||
|
||||
/**
|
||||
MESSAGE INPUT
|
||||
*/
|
||||
|
||||
.input-wrapper {
|
||||
display: table;
|
||||
width: 100%;
|
||||
padding: 0 0 0 8px;
|
||||
}
|
||||
.input-wrapper .input-message,
|
||||
.input-wrapper .input-prio,
|
||||
.input-wrapper .input-button {
|
||||
display: table-cell;
|
||||
text-align: left;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
.input-wrapper .input-message {
|
||||
width: 85%;
|
||||
}
|
||||
.input-wrapper .input-prio {
|
||||
width: 10%;
|
||||
white-space: nowrap;
|
||||
padding: 0 6px;
|
||||
}
|
||||
.input-wrapper .input-button {
|
||||
width: 5%;
|
||||
}
|
||||
#nachricht {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
}
|
||||
@@ -0,0 +1,863 @@
|
||||
var Chat = function ($, PushJS) {
|
||||
"use strict";
|
||||
|
||||
var me = {
|
||||
|
||||
settings: {
|
||||
// Interval nach dem neue Nachrichten abgerufen werden (in Millisekunden)
|
||||
fetchMessageInterval: 2000,
|
||||
|
||||
// Interval nach dem die Userliste aktualisiert wird (in Millisekunden)
|
||||
fetchUserlistInterval: 15000,
|
||||
|
||||
// Zeit die zwischen zwei LazyLoading-Requests vergehen muss (in Millisekunden)
|
||||
lazyLoadingBufferTime: 250,
|
||||
|
||||
// Zeit für die eine neue Nachricht als "NEU" markiert ist (in Millisekunden)
|
||||
newTimeout: 5000
|
||||
},
|
||||
|
||||
selector: {
|
||||
// Nachrichtenbereich
|
||||
messageArea: '#message-area',
|
||||
|
||||
// Eingabefeld für neue Nachrichten
|
||||
inputField: '#nachricht',
|
||||
|
||||
// Container in dem die Benutzerliste angezeigt wird
|
||||
userList: '#userlist',
|
||||
|
||||
// Container in dem die Raumliste angezeigt wird
|
||||
roomList: '#roomlist',
|
||||
|
||||
// Bezeichner für den aktuellen Raum
|
||||
roomName: '#message-header',
|
||||
|
||||
// Formular-Element
|
||||
chatForm: '#chatform',
|
||||
|
||||
// Checkbox um Nachricht hervorzuheben
|
||||
prioCheckbox: '#prio',
|
||||
|
||||
// InfoBox für Browser-Benachrichtigungen
|
||||
notificationInfo: '#notification-info',
|
||||
|
||||
// Xentral-Sidebar
|
||||
wawiSidebar: '#sidebar'
|
||||
},
|
||||
|
||||
storage: {
|
||||
// User-ID des aktuellen Chat-Partners
|
||||
activeUserId: 0,
|
||||
|
||||
// Eigene User-ID
|
||||
selfUserId: 0,
|
||||
|
||||
// ID der neuesten empfangenen Nachricht
|
||||
newestMessageId: 0,
|
||||
|
||||
// ID der ältesten empfangenen Nachricht
|
||||
oldestMessageId: null,
|
||||
|
||||
// ID der neuesten Gelesen-Markierung
|
||||
newestReadmarkId: 0,
|
||||
|
||||
requestBuffer: 0,
|
||||
|
||||
// Gibt an ob die älteste Nachricht bereits geladen wurde
|
||||
requestEOF: false,
|
||||
|
||||
// Array mit Nachrichten-IDs die als gelesen markiert werden sollen
|
||||
readMark: [],
|
||||
|
||||
// Neuestes Datum; für Anzeige der Datumszeile (wenn zu neueren Nachrichten gescrollt wird)
|
||||
newestDateString: '',
|
||||
|
||||
// Ältestes Datum; für Anzeige der Datumszeile (wenn zu älteren Nachrichten gescrollt wird)
|
||||
oldestDateString: '',
|
||||
|
||||
// Datumswerte merken, die schonmal angezeigt wurden
|
||||
dateStrings: [],
|
||||
|
||||
isStandaloneWindow: null,
|
||||
|
||||
fetchMessageInterval: null,
|
||||
fetchUserlistInterval: null
|
||||
},
|
||||
|
||||
elem: {
|
||||
$window: null,
|
||||
$wrapper: null,
|
||||
$sidebarWrapper: null,
|
||||
$sidebarScroller: null,
|
||||
$messageWrapper: null,
|
||||
$messageScroller: null
|
||||
},
|
||||
|
||||
init: function () {
|
||||
me.storage.isStandaloneWindow = ($(me.selector.wawiSidebar).length === 0);
|
||||
|
||||
me.elem.$window = $(window);
|
||||
me.elem.$wrapper = $('#chat-wrapper');
|
||||
me.elem.$messageWrapper = $('#message-wrapper');
|
||||
me.elem.$messageScroller = $('#message-scroller');
|
||||
me.elem.$sidebarWrapper = $('#sidebar-wrapper');
|
||||
me.elem.$sidebarScroller = $('#sidebar-scroller');
|
||||
|
||||
me.checkNotificationPermission();
|
||||
|
||||
me.resizeElements();
|
||||
me.attachEvents();
|
||||
|
||||
// Chat mit "Öffentlich"
|
||||
me.switchToUser(0);
|
||||
|
||||
if (me.storage.isStandaloneWindow) {
|
||||
var navigation = '<div id="submenu-wrapper" class="clearfix"><div class="back">';
|
||||
navigation += '<a href="index.php?module=welcome&action=start"><svg xmlns="http://www.w3.org/2000/svg" width="7px" height="12px" viewBox="0 0 131.99 218.57"><path fill="none" stroke="#fff" stroke-miterlimit="10" stroke-width="33" d="M120.26 11.65l-97 97.21 97 98.11"/></svg></a>';
|
||||
navigation += '</div><a href="index.php?module=welcome&action=start" style="display:block;padding: 7px 20px;">Zurück zur Startseite</a>';
|
||||
navigation += '</div>';
|
||||
$('#tabs').prepend(navigation);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Registriert alle benötigten Events und Timer
|
||||
*/
|
||||
attachEvents: function () {
|
||||
|
||||
// Formular-Absenden abfangen
|
||||
$(me.selector.chatForm).on('submit', function (event) {
|
||||
event.preventDefault();
|
||||
me.sendMessage($(me.selector.inputField).val(), $(me.selector.prioCheckbox).prop('checked'));
|
||||
});
|
||||
|
||||
// Klick auf User
|
||||
$(document).on('click', me.selector.userList + ' li', function (e) {
|
||||
e.preventDefault();
|
||||
me.switchToUser(parseInt($(this).data('user-id')));
|
||||
});
|
||||
|
||||
// Klick auf Raum
|
||||
$(document).on('click', me.selector.roomList + ' li', function (e) {
|
||||
e.preventDefault();
|
||||
me.switchToUser(parseInt($(this).data('user-id')));
|
||||
});
|
||||
|
||||
// Bei Klick auf InfoBox für Benachrichtigungen > Erlaubnis einholen
|
||||
$(document).on('click', me.selector.notificationInfo, function () {
|
||||
me.requestNotificationPermission();
|
||||
});
|
||||
|
||||
// Fenstergrößen-Änderungen abfangen
|
||||
// Vorallem der Nachrichtenbereich benötigt immer eine feste Breite; ansonsten
|
||||
// können überlange Nachrichten ohne Leerzeichen nicht umgebrochen werden.
|
||||
$(window).on('resize', function () {
|
||||
me.debounce(me.resizeElements, 100);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Startet die Intervale zum Abrufen der Nachrichten und Benutzerliste
|
||||
*/
|
||||
start: function () {
|
||||
// Alle 30 Sekunden die Userliste aktualisieren
|
||||
me.storage.fetchUserlistInterval = setInterval(function () {
|
||||
me.updateUserList();
|
||||
}, me.settings.fetchUserlistInterval);
|
||||
|
||||
// Alle 2 Sekunden die Nachrichten aktualisieren
|
||||
me.storage.fetchMessageInterval = setInterval(function () {
|
||||
me.fetchNewMessages(false);
|
||||
me.setMessagesAsRead();
|
||||
}, me.settings.fetchMessageInterval);
|
||||
},
|
||||
|
||||
/**
|
||||
* Stop die Intervale zum Abrufen der Nachrichten und Benutzerliste
|
||||
*/
|
||||
stop: function () {
|
||||
clearInterval(me.storage.fetchMessageInterval);
|
||||
clearInterval(me.storage.fetchUserlistInterval);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ist Berechtigung zum Senden von Browser-Benachrichtigungen vorhanden?
|
||||
*
|
||||
* @return {boolean}
|
||||
*/
|
||||
checkNotificationPermission: function () {
|
||||
var permissionStatus = PushJS.Permission.get();
|
||||
|
||||
// Falls Berechtigung nicht erteilt oder blockiert wurde > InfoBox anzeigen
|
||||
if (permissionStatus === PushJS.Permission.DEFAULT) {
|
||||
var content =
|
||||
'<div class="info" id="notification-info">' +
|
||||
'<table width="100%" border="0" cellpadding="0" cellspacing="0"><tr>' +
|
||||
'<td>Aktuell können keine Browser-Benachrichtigungen zugestellt werden. ' +
|
||||
'Bitte Klicken Sie hier um die Nachrichten für Xentral zu erlauben.</td>' +
|
||||
'<td align="right"><input type="button" id="notification-button" value="Benachrichtigungen erlauben"></td>'+
|
||||
'</tr></table></div>';
|
||||
$('#tabs-1').prepend(content);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Browser-Dialog zum Erteilen der Berechtigung öffnen
|
||||
*/
|
||||
requestNotificationPermission: function () {
|
||||
PushJS.Permission.request(
|
||||
me.removeNotificationInfoBox, // Berechtigung erteilt
|
||||
me.removeNotificationInfoBox // Berechtigung verweigert
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* InfoBox zu Browser-Benachrichtigungen entfernen
|
||||
*/
|
||||
removeNotificationInfoBox: function () {
|
||||
$(me.selector.notificationInfo).remove();
|
||||
me.resizeElements();
|
||||
},
|
||||
|
||||
/**
|
||||
* Schickt eine neue Nachricht per AJAX ab
|
||||
*
|
||||
* @param {string} message
|
||||
* @param {boolean} priority
|
||||
*/
|
||||
sendMessage: function (message, priority) {
|
||||
if (typeof priority === 'undefined' || typeof priority !== 'boolean') {
|
||||
priority = false;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'index.php?module=chat&action=list&cmd=sendmessage',
|
||||
data: {
|
||||
nachricht: message,
|
||||
empfaenger: me.storage.activeUserId,
|
||||
prio: (priority === true) ? '1' : '0'
|
||||
},
|
||||
success: function () {
|
||||
me.emptyInput();
|
||||
me.focusInput();
|
||||
me.fetchNewMessages(true);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Aktualisiert per AJAX die Userliste
|
||||
*/
|
||||
updateUserList: function () {
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
url: 'index.php?module=chat&action=list&cmd=userlist',
|
||||
success: function (data) {
|
||||
me.renderUserList(data.users);
|
||||
me.renderRoomList(data.rooms)
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Rendert die JSON-Userliste zu HTML
|
||||
*
|
||||
* @param {array} data
|
||||
*/
|
||||
renderUserList: function (data) {
|
||||
var content = '<ul>';
|
||||
|
||||
$(data).each(function (index, user) {
|
||||
user.id = parseInt(user.id);
|
||||
|
||||
// Sich selbst nicht in Userliste anzeigen
|
||||
if (user.self === true) {
|
||||
me.storage.selfUserId = user.id;
|
||||
return;
|
||||
}
|
||||
|
||||
if (user.id === me.storage.activeUserId) {
|
||||
var roomname = '<img src="index.php?module=ajax&action=profilbild&id=' + user.id + '" alt="' + user.name + '">';
|
||||
roomname += '<span>Mit ' + user.name + '</span>';
|
||||
$(me.selector.roomName).html(roomname).removeClass('public');
|
||||
}
|
||||
|
||||
var classes = (user.online === true) ? 'online' : '';
|
||||
classes += (user.id === me.storage.activeUserId) ? ' active' : '';
|
||||
|
||||
content += '<li class="' + classes + '" data-user-id="' + user.id + '">';
|
||||
content += '<span class="name">' + user.name + '</span> ';
|
||||
if (user.unread > 0) {
|
||||
content += '<span class="' + (user.unread > 0 ? 'unread' : '') + '">(' + user.unread + ')</span>';
|
||||
}
|
||||
content += '</li>';
|
||||
});
|
||||
|
||||
content += '</ul>';
|
||||
|
||||
$(me.selector.userList).html(content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Rendert die JSON-Raumliste zu HTML
|
||||
*
|
||||
* @param {array} data
|
||||
*/
|
||||
renderRoomList: function (data) {
|
||||
var content = '<ul>';
|
||||
|
||||
$(data).each(function (index, room) {
|
||||
var classes = (room.id === me.storage.activeUserId) ? ' active' : '';
|
||||
if (room.id === me.storage.activeUserId) {
|
||||
$(me.selector.roomName).html('<span>' + room.name + '</span>').addClass('public');
|
||||
}
|
||||
|
||||
content += '<li class="' + classes + '" data-user-id="' + room.id + '">';
|
||||
content += '<span class="name">' + room.name + '</span> ';
|
||||
if (room.unread > 0) {
|
||||
content += '<span class="' + (room.unread > 0 ? 'unread' : '') + '">(' + room.unread + ')</span>';
|
||||
}
|
||||
content += '</li>';
|
||||
});
|
||||
|
||||
content += '</ul>';
|
||||
|
||||
$(me.selector.roomList).html(content);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ruft neue Nachrichten per AJAX ab
|
||||
*
|
||||
* @param {boolean} scrollDown Soll nach dem Aktualisieren der Nachrichten zur neuesten Nachricht gescrollt werden?
|
||||
*/
|
||||
fetchNewMessages: function (scrollDown) {
|
||||
if (typeof scrollDown === 'undefined' || typeof scrollDown !== 'boolean') {
|
||||
scrollDown = false;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'index.php?module=chat&action=list&cmd=messages',
|
||||
data: {
|
||||
user_id: me.storage.activeUserId,
|
||||
after_message_id: me.storage.newestMessageId,
|
||||
after_readmark_id: me.storage.newestReadmarkId
|
||||
},
|
||||
success: function (data) {
|
||||
me.renderNewMessages(data.messages);
|
||||
me.renderMessagesAsRead(data.readmark);
|
||||
|
||||
if (scrollDown === true) {
|
||||
me.scrollToLatestMessage();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Nachrichten als "Gelesen" markieren
|
||||
*
|
||||
* renderNewMessages() füllt das readMark-Array
|
||||
*/
|
||||
setMessagesAsRead: function () {
|
||||
if (me.storage.readMark.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'index.php?module=chat&action=list&cmd=markread',
|
||||
data: {
|
||||
messages: me.storage.readMark
|
||||
},
|
||||
success: function (data) {
|
||||
// PHP liefert die Nachrichten-IDs der Gelesen-markierten Nachrichten zurück
|
||||
$(data).each(function (index, messageId) {
|
||||
messageId = parseInt(messageId);
|
||||
var pos = me.storage.readMark.indexOf(messageId);
|
||||
if (pos !== -1) {
|
||||
// Bestätigt-Gelesene Nachricht aus Array entfernen
|
||||
me.storage.readMark.splice(pos, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Setzt bereits gerenderte Nachrichten auf "Gelesen"
|
||||
*
|
||||
* @param {array} data
|
||||
*/
|
||||
renderMessagesAsRead: function (data) {
|
||||
if (data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Im öffentlichen Chat gibt es keine Gelesen-Markierungen
|
||||
if (me.isPublicChat()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$(data).each(function (index, readmark) {
|
||||
if (readmark.id > me.storage.newestReadmarkId) {
|
||||
me.storage.newestReadmarkId = readmark.id;
|
||||
}
|
||||
|
||||
// Nachricht als Gelesen markieren
|
||||
$(me.selector.messageArea)
|
||||
.find('#message-' + readmark.message)
|
||||
.removeClass('unread')
|
||||
.addClass('read');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Ruft ältere Nachrichten per AJAX ab; wenn man nach oben scrollt
|
||||
*/
|
||||
fetchOldMessages: function () {
|
||||
// Die älteste Nachricht wurde bereits erreicht
|
||||
if (me.storage.requestEOF === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Request-Puffer ist aktiv; du musst noch warten
|
||||
if (me.storage.requestBuffer > window.performance.now()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Puffer setzen; Nur ein Request alle 0,25 Sekunden
|
||||
// Notwendig weil Scroll-Event mehrmals pro Sekunde ausgelöst wird.
|
||||
me.storage.requestBuffer = window.performance.now() + me.settings.lazyLoadingBufferTime;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: 'index.php?module=chat&action=list&cmd=messages',
|
||||
data: {
|
||||
user_id: me.storage.activeUserId,
|
||||
before_message_id: me.storage.oldestMessageId
|
||||
},
|
||||
success: function (data) {
|
||||
if (data.messages.length === 0) {
|
||||
// Ende wurde erreicht; es gibt keine älteren Nachrichten
|
||||
me.renderDateString(me.storage.oldestDateString, 'DESC', true);
|
||||
me.storage.requestEOF = true;
|
||||
return;
|
||||
}
|
||||
|
||||
me.renderOldMessages(data.messages);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Rendert die übergebene JSON-Nachrichtenliste zu HTML
|
||||
*
|
||||
* @param {array} data
|
||||
*/
|
||||
renderNewMessages: function (data) {
|
||||
var newMessages = false;
|
||||
|
||||
// Abbruch wenn keine neuen Nachrichten im JSON sind
|
||||
if (data.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (me.storage.oldestMessageId === null) {
|
||||
me.storage.oldestMessageId = data[0].id;
|
||||
}
|
||||
|
||||
$(data).each(function (index, msg) {
|
||||
|
||||
// Zeile mit Datum einfügen
|
||||
me.renderDateString(msg.date, 'ASC', false);
|
||||
|
||||
// Nachricht in Nachrichtenbereich anfügen
|
||||
me.renderSingleMessage(msg, 'ASC');
|
||||
|
||||
newMessages = true;
|
||||
|
||||
// Ungelesene Nachrichten für Gelesen-Markierung vormerken
|
||||
if (msg.read === false) {
|
||||
me.reserveMessageForReadMark(msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Nach unten scrollen, falls eine neue Nachricht dabei war
|
||||
if (newMessages === true) {
|
||||
me.scrollToLatestMessage();
|
||||
|
||||
setTimeout(function () {
|
||||
me.enableScrollSpy();
|
||||
}, 100);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Rendert die übergebene JSON-Nachrichtenliste zu HTML
|
||||
*
|
||||
* @param {array} data
|
||||
*/
|
||||
renderOldMessages: function (data) {
|
||||
// Höhe des Nachrichtenbereich merken
|
||||
var oldHeight = $(me.selector.messageArea).height();
|
||||
|
||||
data = data.reverse();
|
||||
$(data).each(function (index, msg) {
|
||||
me.renderDateString(msg.date, 'DESC', false);
|
||||
me.renderSingleMessage(msg, 'DESC');
|
||||
|
||||
// Ungelesene Nachrichten für Gelesen-Markierung vormerken
|
||||
if (msg.read === false) {
|
||||
me.reserveMessageForReadMark(msg);
|
||||
}
|
||||
});
|
||||
|
||||
// Scroll-Position vor dem Einfügen anzeigen
|
||||
var newHeight = $(me.selector.messageArea).height();
|
||||
var scrollTop = (newHeight - oldHeight) - 20;
|
||||
me.elem.$messageScroller.scrollTop(scrollTop);
|
||||
},
|
||||
|
||||
/**
|
||||
* Nachricht für Gelesen-Markierung vormerken
|
||||
*
|
||||
* @param {object} msg
|
||||
*/
|
||||
reserveMessageForReadMark: function (msg) {
|
||||
if (msg.read === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ungelesene Nachrichten für Gelesen-Markierung vormerken, wenn
|
||||
// ... Chat mit User
|
||||
if (me.isPrivateChat() && !me.isOwnMessage(msg.user)) {
|
||||
me.storage.readMark.push(msg.id);
|
||||
}
|
||||
// .. Chat mit Öffentlich
|
||||
if (me.isPublicChat()) {
|
||||
me.storage.readMark.push(msg.id);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {object} msg
|
||||
* @param {string} direction [ASC|DESC]
|
||||
*/
|
||||
renderSingleMessage: function (msg, direction) {
|
||||
|
||||
// Abbrechen, wenn Nachricht bereits vorhanden ist
|
||||
if ($('#message-' + msg.id).length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// CSS-Klassen zusammenstellen
|
||||
var classes = 'message';
|
||||
if (me.isPrivateChat()) {
|
||||
// Bei eigenen Nachrichten anzeigen ob Nachricht gelesen wurde
|
||||
if (me.isOwnMessage(msg.user)) {
|
||||
if (msg.read === false) {
|
||||
classes += ' unread';
|
||||
}
|
||||
if (msg.read === true) {
|
||||
classes += ' read';
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fremde ungelesene Nachrichten als NEU markieren
|
||||
if (!me.isOwnMessage(msg.user)) {
|
||||
if (msg.read === false) {
|
||||
classes += ' new';
|
||||
}
|
||||
}
|
||||
// Prio-Nachrichten markieren
|
||||
if (msg.prio === true) {
|
||||
classes += ' prio';
|
||||
}
|
||||
|
||||
// Nachrichten-HTML zusammenstellen
|
||||
var content =
|
||||
'<div data-id="{{id}}" id="{{elementId}}" class="{{classes}}">' +
|
||||
' <div class="image">' +
|
||||
' <img src="index.php?module=ajax&action=profilbild&id={{user}}" alt="{{alt}}">' +
|
||||
' </div>' +
|
||||
' <div class="head">' +
|
||||
' <span class="sender">{{name}}</span>' +
|
||||
' <span class="time">{{time}}</span>' +
|
||||
' </div>' +
|
||||
' <div class="text">{{text}}</div>' +
|
||||
'</div>';
|
||||
content = content.replace('{{id}}', msg.id);
|
||||
content = content.replace('{{user}}', msg.user);
|
||||
content = content.replace('{{alt}}', msg.name);
|
||||
content = content.replace('{{name}}', msg.name);
|
||||
content = content.replace('{{time}}', msg.time);
|
||||
content = content.replace('{{text}}', msg.text);
|
||||
content = content.replace('{{classes}}', classes);
|
||||
content = content.replace('{{elementId}}', 'message-' + msg.id);
|
||||
|
||||
// Neue Nachrichten unten anhängen
|
||||
if (direction === 'ASC') {
|
||||
$(me.selector.messageArea).append(content);
|
||||
}
|
||||
// Ältere Nachrichten oben einfügen
|
||||
if (direction === 'DESC') {
|
||||
$(me.selector.messageArea).prepend(content);
|
||||
}
|
||||
|
||||
// NEU-Markierung nach fünf Sekunden wieder entfernen
|
||||
setTimeout(function () {
|
||||
$('#message-' + msg.id + '.new').removeClass('new');
|
||||
}, me.settings.newTimeout);
|
||||
|
||||
// ID der ältesten und neuesten Nachricht wegspeichern
|
||||
if (msg.id > me.storage.newestMessageId) {
|
||||
me.storage.newestMessageId = msg.id
|
||||
}
|
||||
if (msg.id < me.storage.oldestMessageId) {
|
||||
me.storage.oldestMessageId = msg.id
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Zeile mit Datum in Nachrichtenbereich einfügen
|
||||
*
|
||||
* @param {string} dateString
|
||||
* @param {string} direction [ASC|DESC]
|
||||
* @param {boolean} force Datum immer ausgeben
|
||||
*/
|
||||
renderDateString: function (dateString, direction, force) {
|
||||
if (typeof direction === 'undefined' || typeof direction !== 'string') {
|
||||
direction = 'ASC';
|
||||
}
|
||||
if (typeof force === 'undefined' || typeof force !== 'boolean') {
|
||||
force = false;
|
||||
}
|
||||
|
||||
// Datum wurde bereits ausgegeben
|
||||
if (me.storage.dateStrings.indexOf(dateString) !== -1) {
|
||||
return;
|
||||
}
|
||||
// Merken welche Datumseinträge bereits angezeigt wurden
|
||||
me.storage.dateStrings.push(dateString);
|
||||
|
||||
if (me.storage.newestDateString === '') {
|
||||
me.storage.newestDateString = dateString;
|
||||
}
|
||||
if (me.storage.oldestDateString === '') {
|
||||
me.storage.oldestDateString = dateString;
|
||||
}
|
||||
|
||||
// Aufsteigend, wenn neue Nachrichten eingefügt werden
|
||||
if (direction === 'ASC') {
|
||||
if (me.storage.newestDateString !== dateString) {
|
||||
$(me.selector.messageArea).append(
|
||||
'<div class="date"><span>' + dateString + '</span></div>'
|
||||
);
|
||||
me.storage.newestDateString = dateString;
|
||||
}
|
||||
}
|
||||
|
||||
// Absteigend, wenn ältere Nachrichten eingefügt werden
|
||||
if (direction === 'DESC') {
|
||||
if (me.storage.oldestDateString !== dateString || force === true) {
|
||||
$(me.selector.messageArea).prepend(
|
||||
'<div class="date"><span>' + me.storage.oldestDateString + '</span></div>'
|
||||
);
|
||||
me.storage.oldestDateString = dateString;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Scrollt zur neuesten Nachricht
|
||||
*/
|
||||
scrollToLatestMessage: function () {
|
||||
me.elem.$messageScroller.animate({
|
||||
scrollTop: me.elem.$messageScroller.get(0).scrollHeight
|
||||
}, 100);
|
||||
},
|
||||
|
||||
/**
|
||||
* Wechselt den Chat-Partner
|
||||
*
|
||||
* @param {number} userId
|
||||
*/
|
||||
switchToUser: function (userId) {
|
||||
// Storage-Variablen zurücksetzen
|
||||
me.storage.activeUserId = userId;
|
||||
me.storage.newestMessageId = 0;
|
||||
me.storage.oldestMessageId = null;
|
||||
me.storage.oldestDateString = '';
|
||||
me.storage.newestDateString = '';
|
||||
me.storage.newestReadmarkId = 0;
|
||||
me.storage.readMark = [];
|
||||
me.storage.dateStrings = [];
|
||||
me.storage.requestBuffer = 0;
|
||||
me.storage.requestEOF = false;
|
||||
|
||||
// Intervalle anhalten
|
||||
me.stop();
|
||||
|
||||
me.updateUserList();
|
||||
me.emptyMessageArea();
|
||||
me.fetchNewMessages(true);
|
||||
me.emptyInput();
|
||||
me.focusInput();
|
||||
|
||||
// Intervalle nach zwei Sekunden starten.
|
||||
// Ansonsten kann der Nachrichtenempfang des ersten Intervalls und das
|
||||
// obige fetchNewMessages() zeitgleich passieren. Das führt zu leeren
|
||||
// Datumseinträgen.
|
||||
window.setTimeout(function () {
|
||||
me.start();
|
||||
}, 2000);
|
||||
},
|
||||
|
||||
/**
|
||||
* Höhe und Breite von Elementen festlegen
|
||||
*
|
||||
* Methode wird beim Vergößern/-kleinern des Fensters aufgerufen.
|
||||
*
|
||||
* Vorallem der Nachrichtenbereich benötigt eine feste Breite, um überlangen Text
|
||||
* ohne Whitespace zuverlässig umbrechen zu können.
|
||||
*/
|
||||
resizeElements: function () {
|
||||
var windowHeight = me.elem.$window.height();
|
||||
var wrapperWidth = me.elem.$wrapper.width();
|
||||
var sidebarWidth = me.elem.$sidebarWrapper.outerWidth();
|
||||
var $notificationInfo = $(me.selector.notificationInfo);
|
||||
var notificationHeight = ($notificationInfo.outerHeight() !== null) ? $notificationInfo.outerHeight() + 10 : 0;
|
||||
var newMessageWidth = 0;
|
||||
var newMessageHeight = 0;
|
||||
var newSidebarHeight = 0;
|
||||
|
||||
if (me.storage.isStandaloneWindow) {
|
||||
newMessageWidth = wrapperWidth - sidebarWidth - 10;
|
||||
newMessageHeight = windowHeight - 200;
|
||||
newSidebarHeight = windowHeight - 90;
|
||||
|
||||
} else {
|
||||
newMessageWidth = wrapperWidth - sidebarWidth - 10;
|
||||
newMessageHeight = windowHeight - 360;
|
||||
newSidebarHeight = windowHeight - 240;
|
||||
}
|
||||
|
||||
newMessageHeight -= notificationHeight;
|
||||
newSidebarHeight -= notificationHeight;
|
||||
|
||||
me.elem.$messageScroller.width(newMessageWidth);
|
||||
me.elem.$messageScroller.height(newMessageHeight);
|
||||
me.elem.$sidebarScroller.height(newSidebarHeight);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ältere Nachrichten beim Nach-Oben-Scrollen abrufen
|
||||
*/
|
||||
enableScrollSpy: function () {
|
||||
// Ermitteln ob Nachrichtenbereich überhaupt einen Scrollbalken hat;
|
||||
// Es gibt keinen Scrollbalken bei wenigen oder keinen Nachrichten.
|
||||
var messageAreaHasScrollbar =
|
||||
(me.elem.$messageScroller.height() < $(me.selector.messageArea).height());
|
||||
|
||||
// ScrollSpy wird nicht benötigt wenn kein Scrollbalken vorhanden
|
||||
if (messageAreaHasScrollbar === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ältere Nachrichten abrufen wenn oberer Nachrichtenbereich angezeigt wird
|
||||
me.elem.$messageScroller.on('scroll', function () {
|
||||
var scrollTop = $(this).scrollTop();
|
||||
if (scrollTop < 50) {
|
||||
me.fetchOldMessages();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll-Event auf Nachrichtenbereich entfernen
|
||||
*/
|
||||
disableScrollSpy: function () {
|
||||
me.elem.$messageScroller.off('scroll');
|
||||
},
|
||||
|
||||
/**
|
||||
* Setzt den Focus auf das Eingabefeld
|
||||
*/
|
||||
focusInput: function () {
|
||||
$(me.selector.inputField).trigger('focus');
|
||||
},
|
||||
|
||||
/**
|
||||
* Eingabefeld leeren
|
||||
*/
|
||||
emptyInput: function () {
|
||||
$(me.selector.inputField).val('');
|
||||
//$(me.selector.prioCheckbox).prop('checked', '');
|
||||
// Prio-Checkbox nicht zurücksetzen, soll manuell durch Benutzer passieren.
|
||||
},
|
||||
|
||||
/**
|
||||
* Leert den Nachrichtenbereich
|
||||
*/
|
||||
emptyMessageArea: function () {
|
||||
// Eventuell vorhandenen Scroll-Event entfernen
|
||||
me.disableScrollSpy();
|
||||
|
||||
// Nachrichtenbereich leeren
|
||||
$(me.selector.messageArea).empty();
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {bool}
|
||||
*/
|
||||
isPublicChat: function () {
|
||||
return me.storage.activeUserId === 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @return {bool}
|
||||
*/
|
||||
isPrivateChat: function () {
|
||||
return me.storage.activeUserId > 0;
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {number} senderId
|
||||
* @return {bool}
|
||||
*/
|
||||
isOwnMessage: function (senderId) {
|
||||
return senderId === me.storage.selfUserId;
|
||||
},
|
||||
|
||||
/**
|
||||
* Puffer-Funktion um Events erst nach einer bestimmten Zeit auszuführen
|
||||
*
|
||||
* @param {function} callback
|
||||
* @param {number} delay
|
||||
*/
|
||||
debounce: function (callback, delay) {
|
||||
var context = this;
|
||||
var args = arguments;
|
||||
|
||||
window.clearTimeout(me.storage.buffer);
|
||||
me.storage.buffer = window.setTimeout(function () {
|
||||
callback.apply(context, args);
|
||||
}, delay || 250);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
init: me.init,
|
||||
start: me.start,
|
||||
stop: me.stop
|
||||
};
|
||||
|
||||
}(jQuery, Push);
|
||||
|
||||
$(document).ready(function () {
|
||||
Chat.init();
|
||||
});
|
||||
Reference in New Issue
Block a user