From 4356478ae84454e78a9888270ce68d1bcb3d3e84 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:36:53 +1030 Subject: [PATCH 01/85] Adding private message sound capability --- chat/js/config.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/chat/js/config.js b/chat/js/config.js index a4d3c3f..d1863d7 100644 --- a/chat/js/config.js +++ b/chat/js/config.js @@ -102,6 +102,8 @@ var ajaxChatConfig = { soundChatBot: 'sound_5', // Defines the sound that is played on error messages: soundError: 'sound_6', + // Defines the sound that is played when private messages are received: + soundPrivate: 'sound_7', // Defines if the document title blinks on new messages: blink: true, @@ -208,7 +210,8 @@ var ajaxChatConfig = { sound_3: 'sound_3.mp3', sound_4: 'sound_4.mp3', sound_5: 'sound_5.mp3', - sound_6: 'sound_6.mp3' + sound_6: 'sound_6.mp3', + sound_7: 'sound_5.mp3' }, @@ -258,4 +261,4 @@ var ajaxChatConfig = { // This ID can be used to distinguish between different chat installations using the same socket server: socketServerChatID: 0 -} \ No newline at end of file +} From 8252091ee322889d453dad6a419d77e10c455db6 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:45:05 +1030 Subject: [PATCH 02/85] Adding private message sound capability --- chat/js/chat.js | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index 9a93f5e..ebc6c54 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -691,7 +691,14 @@ var ajaxChat = { this.playSound(this.settings['soundSend']); break; default: - this.playSound(this.settings['soundReceive']); + var messageParts = messageText.split(' ', 1); + switch(messageParts[0]) { + case '/privmsg': + this.playSound(this.settings['soundPrivate']); + break; + default: + this.playSound(this.settings['soundReceive']); + }; break; } } @@ -2937,4 +2944,4 @@ var ajaxChat = { return true; } -}; \ No newline at end of file +}; From 323a9bb3182dd294c8619384580b07eb11e2be65 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:49:16 +1030 Subject: [PATCH 03/85] Adding private message sound capability --- chat/lib/template/loggedIn.html | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/chat/lib/template/loggedIn.html b/chat/lib/template/loggedIn.html index 16a75eb..71d79f9 100644 --- a/chat/lib/template/loggedIn.html +++ b/chat/lib/template/loggedIn.html @@ -74,6 +74,7 @@ ajaxChat.fillSoundSelection('soundLeaveSetting', ajaxChat.getSetting('soundLeave')); ajaxChat.fillSoundSelection('soundChatBotSetting', ajaxChat.getSetting('soundChatBot')); ajaxChat.fillSoundSelection('soundErrorSetting', ajaxChat.getSetting('soundError')); + ajaxChat.fillSoundSelection('soundPrivateSetting', ajaxChat.getSetting('soundPrivate')); document.getElementById('blinkSetting').checked = ajaxChat.getSetting('blink'); document.getElementById('blinkIntervalSetting').value = ajaxChat.getSetting('blinkInterval'); document.getElementById('blinkIntervalNumberSetting').value = ajaxChat.getSetting('blinkIntervalNumber'); @@ -357,14 +358,20 @@

[LANG]settings[/LANG]

+ + + + + + - + - + @@ -384,4 +391,4 @@

[LANG]settings[/LANG]

- \ No newline at end of file + From c1b791ab6412f80376b17d79602b908c77ed66a4 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:50:44 +1030 Subject: [PATCH 04/85] Adding private message sound capability --- chat/lib/lang/en.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/en.php b/chat/lib/lang/en.php index cb9492b..ed7a152 100644 --- a/chat/lib/lang/en.php +++ b/chat/lib/lang/en.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Search'; $lang['logsPrivateChannels'] = 'Private Channels'; $lang['logsPrivateMessages'] = 'Private Messages'; -?> \ No newline at end of file +?> From e7db436833bc41ca9a0a715e877143c667111e32 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:52:03 +1030 Subject: [PATCH 05/85] Adding private message sound capability --- chat/lib/lang/ar.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chat/lib/lang/ar.php b/chat/lib/lang/ar.php index af9c830..ebcc7a0 100644 --- a/chat/lib/lang/ar.php +++ b/chat/lib/lang/ar.php @@ -6,7 +6,7 @@ * @copyright (c) Sebastian Tschan * @license Modified MIT License * @link https://blueimp.net/ajax/ - */ + */ $lang = array(); $lang['title'] = 'AJAX Chat'; @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'بحث'; $lang['logsPrivateChannels'] = 'القنوات الخاصة'; $lang['logsPrivateMessages'] = 'الرسائل الخاصة'; -?> \ No newline at end of file +?> From 4337df55d9c9c5788e5f5ee74dd4d37cce9adae8 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 04:56:41 +1030 Subject: [PATCH 06/85] Adding private message sound capability --- chat/lib/lang/bg.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/bg.php b/chat/lib/lang/bg.php index 46d6af1..8f45afe 100644 --- a/chat/lib/lang/bg.php +++ b/chat/lib/lang/bg.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Звук за съобщенията за излизане от чата или от канала:'; $lang['settingsSoundChatBot'] = 'Звук за съобщенията на чатбота:'; $lang['settingsSoundError'] = 'Звук за съобщенията за грешки:'; +$lang['settingsSoundPrivate'] = 'Звук за лични за съобщенията:'; $lang['settingsBlink'] = 'Примигване на прозоречното заглавие при нови съобщения:'; $lang['settingsBlinkInterval'] = 'Интервал на примигване в милисекунди:'; $lang['settingsBlinkIntervalNumber'] = 'Брой пъти на примигване:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Търсене'; $lang['logsPrivateChannels'] = 'Лични канали'; $lang['logsPrivateMessages'] = 'Лични съобщения'; -?> \ No newline at end of file +?> From e4f562e78895cfc0f26f7b5ac244f62c18dc548e Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:00:36 +1030 Subject: [PATCH 07/85] Adding private message sound capability --- chat/lib/lang/ca.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/ca.php b/chat/lib/lang/ca.php index e45c376..87b1be5 100644 --- a/chat/lib/lang/ca.php +++ b/chat/lib/lang/ca.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'So per desconnectar i missatges de sortida del canal:'; $lang['settingsSoundChatBot'] = 'So pels missatges del xat:'; $lang['settingsSoundError'] = 'So per missatges d\'error:'; +$lang['settingsSoundPrivate'] = 'So per missatges personals:'; $lang['settingsBlink'] = 'Notificació de finestra per als nous missatges:'; $lang['settingsBlinkInterval'] = 'Interval de notificacions intermitents (milisegons):'; $lang['settingsBlinkIntervalNumber'] = 'Nombre de notificacions intermitents:'; @@ -122,4 +123,4 @@ $lang['logsPrivateChannels'] = 'Canals privats'; $lang['logsPrivateMessages'] = 'Missatges privats'; -?> \ No newline at end of file +?> From e24e33aa90807844f339fefd14f5cf134eb3b44a Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:02:52 +1030 Subject: [PATCH 08/85] Adding private message sound capability --- chat/lib/lang/cy.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/cy.php b/chat/lib/lang/cy.php index dea446d..4fa92f4 100644 --- a/chat/lib/lang/cy.php +++ b/chat/lib/lang/cy.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sain am allgofnodi a negeseuon gadael sianeli:'; $lang['settingsSoundChatBot'] = 'Sain am negeseuon sgwrsbot:'; $lang['settingsSoundError'] = 'Sain am wallnegeseuon:'; +$lang['settingsSoundPrivate'] = 'Sain am negeseuon preifat:'; $lang['settingsBlink'] = 'Fflachio teitl y ffenestr ar negeseuon newydd:'; $lang['settingsBlinkInterval'] = 'Cyfwng fflachio mewn milieiliadau:'; $lang['settingsBlinkIntervalNumber'] = 'Nifer y cyfyngau fflachio:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Chwilio'; $lang['logsPrivateChannels'] = 'Sianeli Preifat'; $lang['logsPrivateMessages'] = 'Negeseuon Preifat'; -?> \ No newline at end of file +?> From 9f2a9bdf86da5f698280b8ba7006d4baa56f9fa1 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:05:34 +1030 Subject: [PATCH 09/85] Adding private message sound capability --- chat/lib/lang/cz.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/cz.php b/chat/lib/lang/cz.php index cc6868c..097d1c9 100644 --- a/chat/lib/lang/cz.php +++ b/chat/lib/lang/cz.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Zvuk při odhlášení nebo odchodu z místnosti:'; $lang['settingsSoundChatBot'] = 'Zvuk při zprávě chatbota:'; $lang['settingsSoundError'] = 'Zvuk při chybové zprávě:'; +$lang['settingsSoundPrivate'] = 'Zvuk soukromých zpráv:'; $lang['settingsBlink'] = 'Blikání titulku okna při příchozí zprávě:'; $lang['settingsBlinkInterval'] = 'Interval blikání v milisekundách:'; $lang['settingsBlinkIntervalNumber'] = 'Počet bliknutí:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Hledej'; $lang['logsPrivateChannels'] = 'Soukromé místnosti'; $lang['logsPrivateMessages'] = 'Soukromé zprávy'; -?> \ No newline at end of file +?> From ffcdce88177b8ba124d882c6a57c68fc0b44b2fd Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:10:49 +1030 Subject: [PATCH 10/85] Adding private message sound capability --- chat/lib/lang/da.php | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/lib/lang/da.php b/chat/lib/lang/da.php index 55e7eff..ad5a453 100644 --- a/chat/lib/lang/da.php +++ b/chat/lib/lang/da.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Lyd ved login og forlad kanal beskeder:'; $lang['settingsSoundChatBot'] = 'Lyd ved ChatBot beskeder:'; $lang['settingsSoundError'] = 'Lyd ved fejlmeddelse:'; +$lang['settingsSoundPrivate'] = 'Lyd ved private beskeder:'; $lang['settingsBlink'] = 'Blink vindue titel ved nye beskeder:'; $lang['settingsBlinkInterval'] = 'Blink interval i millisekonder:'; $lang['settingsBlinkIntervalNumber'] = 'Antal blink intervaler:'; From 6f5bf5a79710bcd5865e965481d0844afa5f9175 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:10:53 +1030 Subject: [PATCH 11/85] Adding private message sound capability --- chat/lib/lang/de.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/de.php b/chat/lib/lang/de.php index f345d10..a126983 100644 --- a/chat/lib/lang/de.php +++ b/chat/lib/lang/de.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Klang für Logouts und das Verlassen von Räumen:'; $lang['settingsSoundChatBot'] = 'Klang für Chatbot Nachrichten:'; $lang['settingsSoundError'] = 'Klang für Fehlermeldungen:'; +$lang['settingsSoundPrivate'] = 'Klang für private Nachrichten:'; $lang['settingsBlink'] = 'Blinkender Fenstertitel bei neuen Nachrichten:'; $lang['settingsBlinkInterval'] = 'Blink-Intervall in Millisekunden:'; $lang['settingsBlinkIntervalNumber'] = 'Anzahl der Blink-Intervalle:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Suche'; $lang['logsPrivateChannels'] = 'Private Räume'; $lang['logsPrivateMessages'] = 'Private Nachrichten'; -?> \ No newline at end of file +?> From 3577981aa6c76e863e8612b7a83a070d7eab78d2 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:10:57 +1030 Subject: [PATCH 12/85] Adding private message sound capability --- chat/lib/lang/el.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/el.php b/chat/lib/lang/el.php index 526c31b..7c6cc94 100644 --- a/chat/lib/lang/el.php +++ b/chat/lib/lang/el.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Ήχος για μηνύματα αποχώρισης και αποσύνδεσης:'; $lang['settingsSoundChatBot'] = 'Ήχος για μηνύματα του chatbot:'; $lang['settingsSoundError'] = 'Ήχος για μηνύματα λάθους:'; +$lang['settingsSoundPrivate'] = 'Ήχος για προσωπικά μηνύματα:'; $lang['settingsBlink'] = 'Αναβόσβημα τίτλου σε νέα μηνύματα:'; $lang['settingsBlinkInterval'] = 'Ρυθμός αναβοσβήματος σε χιλιοστά του δευτερόλεπτου:'; $lang['settingsBlinkIntervalNumber'] = 'Αριθμός αναβοσβήματος οθόνης:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Αναζήτηση'; $lang['logsPrivateChannels'] = 'Πριβέ κανάλια'; $lang['logsPrivateMessages'] = 'προσωπικά μηνύματα'; -?> \ No newline at end of file +?> From 8e06c5ab4143f351a54065edfcfb992ad9289d92 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:16:23 +1030 Subject: [PATCH 13/85] Adding private message sound capability --- chat/lib/lang/es.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chat/lib/lang/es.php b/chat/lib/lang/es.php index 27b9b72..65e8e68 100644 --- a/chat/lib/lang/es.php +++ b/chat/lib/lang/es.php @@ -6,7 +6,7 @@ * @copyright (c) Sebastian Tschan * @license Modified MIT License * @link https://blueimp.net/ajax/ - */ + */ $lang = array(); $lang['title'] = 'AJAX Chat'; @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sonido para mensajes de desconexión y salidas del canal:'; $lang['settingsSoundChatBot'] = 'Sonido para mensajes del Chatbot:'; $lang['settingsSoundError'] = 'Sonido para mensajes de error:'; +$lang['settingsSoundPrivate'] = 'Sonido para mensajes privados:'; $lang['settingsBlink'] = 'Parpadeo del título de la ventana con nuevos mensajes:'; $lang['settingsBlinkInterval'] = 'Intervalo de parpadeo en milisegundos:'; $lang['settingsBlinkIntervalNumber'] = 'Número de intervalos de parpadeo:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Buscar'; $lang['logsPrivateChannels'] = 'Canales Privados'; $lang['logsPrivateMessages'] = 'Mensajes Privados'; -?> \ No newline at end of file +?> From 1b056ad85597da52958150ee1e6000305191cc38 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:17:46 +1030 Subject: [PATCH 14/85] Adding private message sound capability --- chat/lib/lang/et.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/et.php b/chat/lib/lang/et.php index f1169fe..1c82fe1 100644 --- a/chat/lib/lang/et.php +++ b/chat/lib/lang/et.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Heli väljumise ja kanalitest lahkumise sõnumitel:'; $lang['settingsSoundChatBot'] = 'Chatboti sõnumite heli:'; $lang['settingsSoundError'] = 'Veateate heli:'; +$lang['settingsSoundPrivate'] = 'Privaatsõnum heli:'; $lang['settingsBlink'] = 'Vilguta akna tiitlit uute sõnumite saabumisel:'; $lang['settingsBlinkInterval'] = 'Vilgutamise intervall millisekundites:'; $lang['settingsBlinkIntervalNumber'] = 'Vilksatuste arv intervallis:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Otsi'; $lang['logsPrivateChannels'] = 'Privaat-kanalid'; $lang['logsPrivateMessages'] = 'Privaat-sõnumid'; -?> \ No newline at end of file +?> From fa25a2a04f8bd43f54e12fe16269f1da0ea13d37 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:17:54 +1030 Subject: [PATCH 15/85] Adding private message sound capability --- chat/lib/lang/fa.php | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/lib/lang/fa.php b/chat/lib/lang/fa.php index 0dc3cb1..a6e45e7 100644 --- a/chat/lib/lang/fa.php +++ b/chat/lib/lang/fa.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; From 6c6ab471e665163f2b8da2fcfe71cfde1d9edef5 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:26:40 +1030 Subject: [PATCH 16/85] Adding private message sound capability --- chat/lib/lang/fi.php | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/lib/lang/fi.php b/chat/lib/lang/fi.php index 73466b4..fc215bb 100644 --- a/chat/lib/lang/fi.php +++ b/chat/lib/lang/fi.php @@ -106,6 +106,7 @@ $lang['settingsSoundLeave'] = 'Ääni poistumiseen keskustelusta ja kanavalta:'; $lang['settingsSoundChatBot'] = 'Ääni chatbotin viesteille:'; $lang['settingsSoundError'] = 'Ääni virheilmoituksille:'; +$lang['settingsSoundPrivate'] = 'Ääni private viesteille:'; $lang['settingsBlink'] = 'Vilkuta ikkunan nimeä uusista viesteistä:'; $lang['settingsBlinkInterval'] = 'Vilkuttamisen aika millisekunneissa:'; $lang['settingsBlinkIntervalNumber'] = 'Vilkutuksen viive:'; From bb973aec70446eb5279a79d1e490ad4033618873 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:28:28 +1030 Subject: [PATCH 17/85] Adding private message sound capability --- chat/lib/lang/fr.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/fr.php b/chat/lib/lang/fr.php index 52ffaf1..e81819e 100644 --- a/chat/lib/lang/fr.php +++ b/chat/lib/lang/fr.php @@ -106,6 +106,7 @@ $lang['settingsSoundLeave'] = 'Son pour déconnexion :'; $lang['settingsSoundChatBot'] = 'Son pour message robot :'; $lang['settingsSoundError'] = 'Son pour les erreurs :'; +$lang['settingsSoundPrivate'] = 'Son message privé:'; $lang['settingsBlink'] = 'Clignoter à chaque nouveau message :'; $lang['settingsBlinkInterval'] = 'Intervalle de clignotage :'; $lang['settingsBlinkIntervalNumber'] = 'Numéro de clignotage :'; @@ -122,4 +123,4 @@ $lang['logsSearch'] = 'Chercher'; $lang['logsPrivateChannels'] = 'Salons privés'; $lang['logsPrivateMessages'] = 'Messages privés'; -?> \ No newline at end of file +?> From 1cfcd6bd03900b12a6c74b26fdb725825d679d3c Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:32:46 +1030 Subject: [PATCH 18/85] Adding private message sound capability --- chat/lib/lang/he.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/he.php b/chat/lib/lang/he.php index 39fa799..30b1a90 100644 --- a/chat/lib/lang/he.php +++ b/chat/lib/lang/he.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'חיפוש'; $lang['logsPrivateChannels'] = 'ערוצים פרטיים'; $lang['logsPrivateMessages'] = 'הודעות פרטיות'; -?> \ No newline at end of file +?> From 6c10fdd78eef2fb6bc939c8e97a3a9b278628eb2 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:34:37 +1030 Subject: [PATCH 19/85] Adding private message sound capability --- chat/lib/lang/it.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/it.php b/chat/lib/lang/it.php index abcd4f9..8e59123 100644 --- a/chat/lib/lang/it.php +++ b/chat/lib/lang/it.php @@ -106,6 +106,7 @@ $lang['settingsSoundLeave'] = 'Suono per uscita:'; $lang['settingsSoundChatBot'] = 'Suono per messaggi robot:'; $lang['settingsSoundError'] = 'Suono per messaggi errore:'; +$lang['settingsSoundPrivate'] = 'Suono per messaggi privati:'; $lang['settingsBlink'] = 'Lampeggio nuovi messaggi:'; $lang['settingsBlinkInterval'] = 'Intervallo lampeggio:'; $lang['settingsBlinkIntervalNumber'] = 'Numero massimo lampeggi:'; @@ -122,4 +123,4 @@ $lang['logsSearch'] = 'Cerca'; $lang['logsPrivateChannels'] = 'Canali Privati'; $lang['logsPrivateMessages'] = 'Messaggi privati'; -?> \ No newline at end of file +?> From 56cf06a8d5c569264614644957a8831d063aa1ad Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:37:07 +1030 Subject: [PATCH 20/85] Adding private message sound capability --- chat/lib/lang/in.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/in.php b/chat/lib/lang/in.php index 175cc90..4b571c0 100644 --- a/chat/lib/lang/in.php +++ b/chat/lib/lang/in.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Suara pada saat keluar Channel:'; $lang['settingsSoundChatBot'] = 'Suara pada saat ChatBot:'; $lang['settingsSoundError'] = 'Suara pada saat terdapat kesalahan:'; +$lang['settingsSoundPrivate'] = 'Suara pada saat privasi:'; $lang['settingsBlink'] = 'Kedipkan Judul Jendela pada saat ada pesan baru:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Jumlah interval kedipan:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Cari'; $lang['logsPrivateChannels'] = 'Saluran-saluran privasi'; $lang['logsPrivateMessages'] = 'Pesan Pribadi'; -?> \ No newline at end of file +?> From 3e809a7ea0076bacde4aa26dcbb5864ff68fa7a3 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:39:37 +1030 Subject: [PATCH 21/85] Adding private message sound capability --- chat/lib/lang/hu.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/hu.php b/chat/lib/lang/hu.php index 5f21741..1b4b0c5 100644 --- a/chat/lib/lang/hu.php +++ b/chat/lib/lang/hu.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Kilépés szobaüzenetek hangja:'; $lang['settingsSoundChatBot'] = 'ChatBot üzenetek hangja:'; $lang['settingsSoundError'] = 'Hibaüzenetek hangja:'; +$lang['settingsSoundPrivate'] = 'Privát üzenetek hangja:'; $lang['settingsBlink'] = 'Ablak fejléce villogjon új üzenet esetén:'; $lang['settingsBlinkInterval'] = 'A villogás intervallumának hossza századmásodpercekben:'; $lang['settingsBlinkIntervalNumber'] = 'Villogási intervallumok száma:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'Keresés'; $lang['logsPrivateChannels'] = 'Privát szobák'; $lang['logsPrivateMessages'] = 'Privát üzenetek'; -?> \ No newline at end of file +?> From b7fe9373299c63e779399c0c9dbb3d9094489c82 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:41:01 +1030 Subject: [PATCH 22/85] Adding private message sound capability --- chat/lib/lang/gl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/gl.php b/chat/lib/lang/gl.php index 54b8a8e..d9c3225 100644 --- a/chat/lib/lang/gl.php +++ b/chat/lib/lang/gl.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Son para logout :'; $lang['settingsSoundChatBot'] = 'Son para mensaxes chat:'; $lang['settingsSoundError'] = 'Son para errores:'; +$lang['settingsSoundPrivate'] = 'Son para mensaxes privadas:'; $lang['settingsBlink'] = 'Parpadeo en ventá con novas mensaxes:'; $lang['settingsBlinkInterval'] = 'Intervalo parpadeos (milisegundos):'; $lang['settingsBlinkIntervalNumber'] = 'Número de parpadeos:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Buscar'; $lang['logsPrivateChannels'] = 'Canles privadas'; $lang['logsPrivateMessages'] = 'mensaxes privadas'; -?> \ No newline at end of file +?> From f205898a818dae22a64fe86210d9cadda918c1b8 Mon Sep 17 00:00:00 2001 From: marquisite Date: Sat, 11 Jan 2014 05:42:23 +1030 Subject: [PATCH 23/85] Adding private message sound capability --- chat/lib/lang/hr.php | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/lib/lang/hr.php b/chat/lib/lang/hr.php index ed04510..31d5c08 100644 --- a/chat/lib/lang/hr.php +++ b/chat/lib/lang/hr.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'Zvuk za odjavljivanje i poruke napuštanja kanala:'; $lang['settingsSoundChatBot'] = 'Zvuk za poruke brbljobota:'; $lang['settingsSoundError'] = 'Zvuk za poruke pogreške:'; +$lang['settingsSoundPrivate'] = 'Zvuk za poruke privatni:'; $lang['settingsBlink'] = 'Treptanje naslova prozora kod novih poruka:'; $lang['settingsBlinkInterval'] = 'Trajanje treptanja (u milisekundama):'; $lang['settingsBlinkIntervalNumber'] = 'Broj treptanja:'; From ed47830e57e487dc78bc9c1b67ccd958bf284493 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:26:37 +1030 Subject: [PATCH 24/85] Adding private message sound capability --- chat/lib/lang/uk.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/chat/lib/lang/uk.php b/chat/lib/lang/uk.php index a7d80c1..ff49519 100644 --- a/chat/lib/lang/uk.php +++ b/chat/lib/lang/uk.php @@ -104,7 +104,8 @@ $lang['settingsSoundEnter'] = 'Звук для події входу в Чат чи кімнату:'; $lang['settingsSoundLeave'] = 'Звук для події виходу з Чату чи кімнати:'; $lang['settingsSoundChatBot'] = 'Звук для системних повідомлень:'; -$lang['settingsSoundError'] = 'Звук для помилок:'; +$lang['settingsSoundError'] = 'Звук для особистих повідомлень:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Сигналізувати при появі нових повідомлень (блимання заголовку вікна):'; $lang['settingsBlinkInterval'] = 'Тривалість блимання заголовку вікна:'; $lang['settingsBlinkIntervalNumber'] = 'Кількість блимань:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Пошук'; $lang['logsPrivateChannels'] = 'Приватні кімнати'; $lang['logsPrivateMessages'] = 'Приватні повідомлення'; -?> \ No newline at end of file +?> From 6f50bf5b8496171800717ef1a562fdf884d21a92 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:28:07 +1030 Subject: [PATCH 25/85] Adding private message sound capability --- chat/lib/lang/sv.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/sv.php b/chat/lib/lang/sv.php index 87f03a0..9509f8b 100644 --- a/chat/lib/lang/sv.php +++ b/chat/lib/lang/sv.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Ljud för Logga ut/lämna Kanal meddelanden:'; $lang['settingsSoundChatBot'] = 'Ljud för Chatbot meddelanden:'; $lang['settingsSoundError'] = 'Ljud för felmeddelanden:'; +$lang['settingsSoundPrivate'] = 'Ljud för privata meddelanden:'; $lang['settingsBlink'] = 'Blinka fönstrets titel vid nya meddelanden:'; $lang['settingsBlinkInterval'] = 'Blinkintervall i millisekunder:'; $lang['settingsBlinkIntervalNumber'] = 'Antal blinkintervaller:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Sök'; $lang['logsPrivateChannels'] = 'Privata Kanaler'; $lang['logsPrivateMessages'] = 'Privata Meddelanden'; -?> \ No newline at end of file +?> From 2ca4310f0fed657d42177d44f6d97ef76374bfb3 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:30:03 +1030 Subject: [PATCH 26/85] Adding private message sound capability --- chat/lib/lang/tr.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/tr.php b/chat/lib/lang/tr.php index 00f7dfa..5986e60 100644 --- a/chat/lib/lang/tr.php +++ b/chat/lib/lang/tr.php @@ -104,6 +104,7 @@ $lang['settingsSoundEnter'] = 'Giriş ve kanala giriş sesi:'; $lang['settingsSoundLeave'] = 'Çıkış ve kanaldan çıkış sesi:'; $lang['settingsSoundChatBot'] = 'Chatbot mesajları sesi:'; +$lang['settingsSoundPrivate'] = 'Özel mesajlar sesi:'; $lang['settingsSoundError'] = 'Hata mesajları sesi:'; $lang['settingsBlink'] = 'Yeni mesaj geldiğinde pencere başlığını kırp:'; $lang['settingsBlinkInterval'] = 'Milisaniye olarak kırpma aralığı:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Ara'; $lang['logsPrivateChannels'] = 'Özel Kanallar'; $lang['logsPrivateMessages'] = 'Özel Mesajlar'; -?> \ No newline at end of file +?> From 1168f3eae0e5c718c646efb71484f1383a666629 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:40:40 +1030 Subject: [PATCH 27/85] Adding private message sound capability --- chat/lib/lang/tr.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/lib/lang/tr.php b/chat/lib/lang/tr.php index 5986e60..ab78e08 100644 --- a/chat/lib/lang/tr.php +++ b/chat/lib/lang/tr.php @@ -104,8 +104,8 @@ $lang['settingsSoundEnter'] = 'Giriş ve kanala giriş sesi:'; $lang['settingsSoundLeave'] = 'Çıkış ve kanaldan çıkış sesi:'; $lang['settingsSoundChatBot'] = 'Chatbot mesajları sesi:'; -$lang['settingsSoundPrivate'] = 'Özel mesajlar sesi:'; $lang['settingsSoundError'] = 'Hata mesajları sesi:'; +$lang['settingsSoundPrivate'] = 'Özel mesajlar sesi:'; $lang['settingsBlink'] = 'Yeni mesaj geldiğinde pencere başlığını kırp:'; $lang['settingsBlinkInterval'] = 'Milisaniye olarak kırpma aralığı:'; $lang['settingsBlinkIntervalNumber'] = 'Kırpma sayısı aralığı:'; From 31e95282711f89470557c48e6150f0b82277e366 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:41:33 +1030 Subject: [PATCH 28/85] Adding private message sound capability --- chat/lib/lang/sk.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/sk.php b/chat/lib/lang/sk.php index fae99a7..632931e 100644 --- a/chat/lib/lang/sk.php +++ b/chat/lib/lang/sk.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Vyhľadávanie'; $lang['logsPrivateChannels'] = 'Súkromný kanál'; $lang['logsPrivateMessages'] = 'Súkromná správa'; -?> \ No newline at end of file +?> From 3c6584540ab3096f68e25e96a0095a3444381aa7 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:42:07 +1030 Subject: [PATCH 29/85] Adding private message sound capability --- chat/lib/lang/ro.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/ro.php b/chat/lib/lang/ro.php index 8e6d641..2ba3dd7 100644 --- a/chat/lib/lang/ro.php +++ b/chat/lib/lang/ro.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Caută'; $lang['logsPrivateChannels'] = 'Canale private'; $lang['logsPrivateMessages'] = 'Mesaje private'; -?> \ No newline at end of file +?> From c3616d5623cf92be3db328e52ff3ff2fbd501a57 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:43:41 +1030 Subject: [PATCH 30/85] Adding private message sound capability --- chat/lib/lang/ru.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/ru.php b/chat/lib/lang/ru.php index ac86531..8b76596 100644 --- a/chat/lib/lang/ru.php +++ b/chat/lib/lang/ru.php @@ -106,6 +106,7 @@ $lang['settingsSoundLeave'] = 'Звук для оповещения об уходе участника из чата:'; $lang['settingsSoundChatBot'] = 'Звук для сообщений бота:'; $lang['settingsSoundError'] = 'Звук для оповещений об ошибках:'; +$lang['settingsSoundPrivate'] = 'Звук для личных сообщений:'; $lang['settingsBlink'] = '"Моргать" заголовком окна при новом сообщении:'; $lang['settingsBlinkInterval'] = 'Интервал "моргания" (в миллисекундах):'; $lang['settingsBlinkIntervalNumber'] = 'Количество "морганий":'; @@ -122,4 +123,4 @@ $lang['logsSearch'] = 'Поиск'; $lang['logsPrivateChannels'] = 'Приватные каналы'; $lang['logsPrivateMessages'] = 'Приватные сообщения'; -?> \ No newline at end of file +?> From 507c17d28c12207c02076cb0d907a5cbd50796e5 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:45:35 +1030 Subject: [PATCH 31/85] Adding private message sound capability --- chat/lib/lang/sr.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/sr.php b/chat/lib/lang/sr.php index a14ba55..69f0661 100644 --- a/chat/lib/lang/sr.php +++ b/chat/lib/lang/sr.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Zvuk za odjavljivanje i izlazak iz sobe:'; $lang['settingsSoundChatBot'] = 'Zvuk za chatbot poruke:'; $lang['settingsSoundError'] = 'Zvuk za poruke o grešci:'; +$lang['settingsSoundPrivate'] = 'Zvuk za privatne poruke:'; $lang['settingsBlink'] = 'Treptanje naziva prozora za nove poruke:'; $lang['settingsBlinkInterval'] = 'Interval treptanja u milisekundama:'; $lang['settingsBlinkIntervalNumber'] = 'Broj intervala treptanja:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Pretraga'; $lang['logsPrivateChannels'] = 'Privatne sobe'; $lang['logsPrivateMessages'] = 'Privatne poruke'; -?> \ No newline at end of file +?> From c1f3493b09a1f16103d896ee9357a70a2cce2bde Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:46:41 +1030 Subject: [PATCH 32/85] Adding private message sound capability --- chat/lib/lang/sl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/sl.php b/chat/lib/lang/sl.php index 6e49415..584b98a 100644 --- a/chat/lib/lang/sl.php +++ b/chat/lib/lang/sl.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Zvok za odjavo in izhod iz sobe:'; $lang['settingsSoundChatBot'] = 'Zvok za chatbot sporočila:'; $lang['settingsSoundError'] = 'Zvuk za sporočila o napakah:'; +$lang['settingsSoundPrivate'] = 'Zvok za zasebna sporočila:'; $lang['settingsBlink'] = 'Trepetanje imena okna za nova sporočila:'; $lang['settingsBlinkInterval'] = 'Interval trepetanja v milisekundah:'; $lang['settingsBlinkIntervalNumber'] = 'Število intervalov trepetanja:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Iskanje'; $lang['logsPrivateChannels'] = 'Privatne sobe'; $lang['logsPrivateMessages'] = 'Privatna sporočila'; -?> \ No newline at end of file +?> From fd2a6b540c8fe9f7890abfc04008835861062912 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:48:56 +1030 Subject: [PATCH 33/85] Adding private message sound capability --- chat/lib/lang/pt-br.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/pt-br.php b/chat/lib/lang/pt-br.php index 034a5fb..a8c0565 100644 --- a/chat/lib/lang/pt-br.php +++ b/chat/lib/lang/pt-br.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Som para logout e avisos de saida em canais:'; $lang['settingsSoundChatBot'] = 'Som para mensagens do chatbot:'; $lang['settingsSoundError'] = 'Som para mensagens de erro:'; +$lang['settingsSoundPrivate'] = 'Som para mensagens do privado:'; $lang['settingsBlink'] = 'Título da janela do piscamento em mensagens novas:'; $lang['settingsBlinkInterval'] = 'Intervalo do piscamento nos milissegundos:'; $lang['settingsBlinkIntervalNumber'] = 'Número de intervalos do piscamento:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Pesquisar'; $lang['logsPrivateChannels'] = 'Canais privados'; $lang['logsPrivateMessages'] = 'Mensagens privadas'; -?> \ No newline at end of file +?> From 7d70b4ca17f41d4d5ed12d0ef38a0d73e43ea8b6 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:55:57 +1030 Subject: [PATCH 34/85] Adding private message sound capability --- chat/lib/lang/pt-pt.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/pt-pt.php b/chat/lib/lang/pt-pt.php index e3399eb..90e70ee 100644 --- a/chat/lib/lang/pt-pt.php +++ b/chat/lib/lang/pt-pt.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Som de logout:'; $lang['settingsSoundChatBot'] = 'Som para mensagens do chatbot:'; $lang['settingsSoundError'] = 'Som para mensagens de erro:'; +$lang['settingsSoundPrivate'] = 'Som para mensagens de private:'; $lang['settingsBlink'] = 'Título da janela do "piscamento" em mensagens novas:'; $lang['settingsBlinkInterval'] = 'Intervalo do "piscamento" em milissegundos:'; $lang['settingsBlinkIntervalNumber'] = 'Número de intervalos do "piscamento":'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Procurar'; $lang['logsPrivateChannels'] = 'Salas privadas'; $lang['logsPrivateMessages'] = 'Mensagens privadas'; -?> \ No newline at end of file +?> From d92ae8bfcee33b930610e19421538011cdc76207 Mon Sep 17 00:00:00 2001 From: marquisite Date: Wed, 15 Jan 2014 15:58:06 +1030 Subject: [PATCH 35/85] Adding private message sound capability --- chat/lib/lang/pl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/pl.php b/chat/lib/lang/pl.php index 92a1716..ba64036 100644 --- a/chat/lib/lang/pl.php +++ b/chat/lib/lang/pl.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Dźwięk dla wyjść z pokoju:'; $lang['settingsSoundChatBot'] = 'Dźwięk dla informacji od bota:'; $lang['settingsSoundError'] = 'Dźwięk dla informacji o błędach:'; +$lang['settingsSoundPrivate'] = 'Dźwięk dla informacji prywatny wiadomości:'; $lang['settingsBlink'] = 'Miganie okienka przy nadejściu nowej wiadomości:'; $lang['settingsBlinkInterval'] = 'Odstęp pomiędzy mignięciami (w milisekundach):'; $lang['settingsBlinkIntervalNumber'] = 'Liczba mignięć:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Szukaj'; $lang['logsPrivateChannels'] = 'Prywatne pokoje'; $lang['logsPrivateMessages'] = 'Prywatne wiadomości'; -?> \ No newline at end of file +?> From e6095c7bd88e022f24bbc71612a9c616e3ea7383 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 05:47:00 +1030 Subject: [PATCH 36/85] Adding private message sound capability --- chat/lib/template/logs.html | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/chat/lib/template/logs.html b/chat/lib/template/logs.html index d0b9162..073ae2b 100644 --- a/chat/lib/template/logs.html +++ b/chat/lib/template/logs.html @@ -73,6 +73,7 @@ ajaxChat.fillSoundSelection('soundLeaveSetting', ajaxChat.getSetting('soundLeave')); ajaxChat.fillSoundSelection('soundChatBotSetting', ajaxChat.getSetting('soundChatBot')); ajaxChat.fillSoundSelection('soundErrorSetting', ajaxChat.getSetting('soundError')); + ajaxChat.fillSoundSelection('soundPrivateSetting', ajaxChat.getSetting('soundPrivate')); document.getElementById('blinkSetting').checked = ajaxChat.getSetting('blink'); document.getElementById('blinkIntervalSetting').value = ajaxChat.getSetting('blinkInterval'); document.getElementById('blinkIntervalNumberSetting').value = ajaxChat.getSetting('blinkIntervalNumber'); @@ -246,14 +247,20 @@

[LANG]settings[/LANG]

+ + + + + + - + - + @@ -273,4 +280,4 @@

[LANG]settings[/LANG]

- \ No newline at end of file + From 43414d8316bdf0c33565e8b579aba5cd43a66c1f Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 05:50:46 +1030 Subject: [PATCH 37/85] Adding private message sound capability --- chat/lib/lang/no.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/no.php b/chat/lib/lang/no.php index 52fe4af..3a40360 100644 --- a/chat/lib/lang/no.php +++ b/chat/lib/lang/no.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Sound for logout and channel leave messages:'; $lang['settingsSoundChatBot'] = 'Sound for chatbot messages:'; $lang['settingsSoundError'] = 'Sound for error messages:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Søk'; $lang['logsPrivateChannels'] = 'Private Kanaler'; $lang['logsPrivateMessages'] = 'Private Meldinger'; -?> \ No newline at end of file +?> From ca396f5a7827686712ee9a8ba5e8473e7889d3c8 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 05:50:50 +1030 Subject: [PATCH 38/85] Adding private message sound capability --- chat/lib/lang/nl-be.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/nl-be.php b/chat/lib/lang/nl-be.php index db01b98..575d640 100644 --- a/chat/lib/lang/nl-be.php +++ b/chat/lib/lang/nl-be.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Geluid voor logout en verlaat berichten:'; $lang['settingsSoundChatBot'] = 'Geluid voor berichten van de bot:'; $lang['settingsSoundError'] = 'Geluid voor errorberichten:'; +$lang['settingsSoundPrivate'] = 'Geluid voor privé berichten:'; $lang['settingsBlink'] = 'Blink window title on new messages:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Zoek'; $lang['logsPrivateChannels'] = 'Privékanalen'; $lang['logsPrivateMessages'] = 'Privéberichten'; -?> \ No newline at end of file +?> From ab6c3a1abf0bb1e0167ba0ab9f9a3fad3505516f Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 05:52:24 +1030 Subject: [PATCH 39/85] Adding private message sound capability --- chat/lib/lang/nl.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/nl.php b/chat/lib/lang/nl.php index c7dcc4b..19c5502 100644 --- a/chat/lib/lang/nl.php +++ b/chat/lib/lang/nl.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Geluid bij logout en verlaten chat:'; $lang['settingsSoundChatBot'] = 'Geluid bij systeenberichten:'; $lang['settingsSoundError'] = 'Geluid bij foutmeldingen:'; +$lang['settingsSoundPrivate'] = 'Geluid bij priveberichten:'; $lang['settingsBlink'] = 'Knipper tekst windowtitel bij nieuw bericht:'; $lang['settingsBlinkInterval'] = 'Knipperinterval in milliseconden:'; $lang['settingsBlinkIntervalNumber'] = 'Aantal keren knippering van tekst:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Zoek'; $lang['logsPrivateChannels'] = 'Privékanalen'; $lang['logsPrivateMessages'] = 'Privéberichten'; -?> \ No newline at end of file +?> From 71fce5831c1a6c75a0eb506f7c74422aa3567574 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 05:54:20 +1030 Subject: [PATCH 40/85] Adding private message sound capability --- chat/lib/lang/mk.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/mk.php b/chat/lib/lang/mk.php index f43bcfb..1762acf 100644 --- a/chat/lib/lang/mk.php +++ b/chat/lib/lang/mk.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'Звук за пораки за излез од четот или од каналот:'; $lang['settingsSoundChatBot'] = 'Звук за пораки на чатботот:'; $lang['settingsSoundError'] = 'Звук за пораки за грешки:'; +$lang['settingsSoundPrivate'] = 'Звук за пораки за приватни:'; $lang['settingsBlink'] = 'Жмигање на заглавието на прозорецот при нови пораки:'; $lang['settingsBlinkInterval'] = 'Интервал на жмигање во милисекунди:'; $lang['settingsBlinkIntervalNumber'] = 'Број пати на жмигање:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'Пребарување'; $lang['logsPrivateChannels'] = 'Приватни канали'; $lang['logsPrivateMessages'] = 'Приватни пораки'; -?> \ No newline at end of file +?> From 89e5e72e34e9daa0eba9a14a89dcf96cfb58f3bc Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:01:55 +1030 Subject: [PATCH 41/85] Private message sound capability (no translation) $lang['settingsSoundPrivate'] requires an English to Thai translation --- chat/lib/lang/th.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/th.php b/chat/lib/lang/th.php index 7ed3b1e..e695944 100644 --- a/chat/lib/lang/th.php +++ b/chat/lib/lang/th.php @@ -105,6 +105,7 @@ $lang['settingsSoundLeave'] = 'เสียงสำหรับออกจากระบบและออกจากห้อง:'; $lang['settingsSoundChatBot'] = 'เสียงสำหรับข้อความจาำกระบบ:'; $lang['settingsSoundError'] = 'เสียงสำหรับข้อความผิดพลาด:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'มีสัญญาณ กระพริบบน Title Bar เมื่อมีข้อความใหม่:'; $lang['settingsBlinkInterval'] = 'Blink interval in milliseconds:'; $lang['settingsBlinkIntervalNumber'] = 'Number of blink intervals:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = 'ค้นหา'; $lang['logsPrivateChannels'] = 'ห้องส่วนตัว'; $lang['logsPrivateMessages'] = 'ข้อความส่วนตัว'; -?> \ No newline at end of file +?> From 06a0f302cbed84827450b2fe5c14a212972d6bb6 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:04:47 +1030 Subject: [PATCH 42/85] Private message sound capability (no translation) --- chat/lib/lang/zh-tw.php | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/lib/lang/zh-tw.php b/chat/lib/lang/zh-tw.php index d39fe6b..17f4bcf 100644 --- a/chat/lib/lang/zh-tw.php +++ b/chat/lib/lang/zh-tw.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = '登出和離開房間的音效:'; $lang['settingsSoundChatBot'] = '系統訊息的音效:'; $lang['settingsSoundError'] = '錯誤訊息的音效:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = '當有新訊息時閃動標題:'; $lang['settingsBlinkInterval'] = '閃動的間隔時間(毫秒):'; $lang['settingsBlinkIntervalNumber'] = '閃動次數:'; From ea3463b68030f760c9598ceba549eae148a6c05a Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:04:50 +1030 Subject: [PATCH 43/85] Private message sound capability (no translation) --- chat/lib/lang/zh.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/zh.php b/chat/lib/lang/zh.php index 1d18f0b..a1b26f9 100644 --- a/chat/lib/lang/zh.php +++ b/chat/lib/lang/zh.php @@ -104,6 +104,7 @@ $lang['settingsSoundEnter'] = '声音提示登录或进入频道信息:'; $lang['settingsSoundLeave'] = '声音提示退出或离开频道信息:'; $lang['settingsSoundChatBot'] = '声音提示机器人信息:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsSoundError'] = '声音提示错误信息:'; $lang['settingsBlink'] = '有新消息时闪烁窗口标题:'; $lang['settingsBlinkInterval'] = '空闲间隔毫秒数:'; @@ -121,4 +122,4 @@ $lang['logsSearch'] = '搜索'; $lang['logsPrivateChannels'] = '私人频道'; $lang['logsPrivateMessages'] = '私人消息'; -?> \ No newline at end of file +?> From c1976f16ab93bf533f07cf8a8132d5537c745b60 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:05:32 +1030 Subject: [PATCH 44/85] Private message sound capability (no translation) --- chat/lib/lang/kr.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/kr.php b/chat/lib/lang/kr.php index 687dd6e..2541659 100644 --- a/chat/lib/lang/kr.php +++ b/chat/lib/lang/kr.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = '로그아웃, 채널 접속해제 소리:'; $lang['settingsSoundChatBot'] = '시스템 메시지 소리:'; $lang['settingsSoundError'] = '오류 소리:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = '새로운 메시지 도착 시 타이틀 깜빡임:'; $lang['settingsBlinkInterval'] = '밀리세컨드 단위 깜빡임 속도:'; $lang['settingsBlinkIntervalNumber'] = '깜빡임 횟수:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = '검색'; $lang['logsPrivateChannels'] = '개인채널'; $lang['logsPrivateMessages'] = '개인 메시지'; -?> \ No newline at end of file +?> From 06847cc688da8469274d103fe3f4da3215050bd7 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:06:28 +1030 Subject: [PATCH 45/85] Private message sound capability (no translation) --- chat/lib/lang/ja.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/ja.php b/chat/lib/lang/ja.php index 075fc4f..e8065da 100644 --- a/chat/lib/lang/ja.php +++ b/chat/lib/lang/ja.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'サウンド - ログアウト、チャンネル退室 :'; $lang['settingsSoundChatBot'] = 'サウンド - チャットボットメッセージ :'; $lang['settingsSoundError'] = 'サウンド - エラーメッセージ :'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = '新着メッセージの到着時にウィンドウタイトルを点滅させる :'; $lang['settingsBlinkInterval'] = '点滅の間隔(ミリ秒) :'; $lang['settingsBlinkIntervalNumber'] = '点滅の回数 :'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = '検索'; $lang['logsPrivateChannels'] = '二人きりモード'; $lang['logsPrivateMessages'] = 'プライベートメッセージ'; -?> \ No newline at end of file +?> From 9d8f8000dc13336acdb4e016eb48acd8c3a09adf Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 06:06:59 +1030 Subject: [PATCH 46/85] Private message sound capability (no translation) --- chat/lib/lang/ka.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/chat/lib/lang/ka.php b/chat/lib/lang/ka.php index d92d508..cd0d09c 100644 --- a/chat/lib/lang/ka.php +++ b/chat/lib/lang/ka.php @@ -104,6 +104,7 @@ $lang['settingsSoundLeave'] = 'გასვლის და არხის მიტოვების ხმა:'; $lang['settingsSoundChatBot'] = 'ჩატბოტის გზავნილების ხმა:'; $lang['settingsSoundError'] = 'შედომის გზავნილების ხმა:'; +$lang['settingsSoundPrivate'] = 'Sound for private messages:'; $lang['settingsBlink'] = 'ახალ გზავნილზე ფანჯრის დასახელების ციმციმი:'; $lang['settingsBlinkInterval'] = 'ციმციმის ინტერვალი მილიწამებში:'; $lang['settingsBlinkIntervalNumber'] = 'ციმციმის ინტერვალების რაოდენობა:'; @@ -120,4 +121,4 @@ $lang['logsSearch'] = 'ძიება'; $lang['logsPrivateChannels'] = 'პირადი არხები'; $lang['logsPrivateMessages'] = 'პირადი გზავნილები'; -?> \ No newline at end of file +?> From da3f23c9cbe5d3c48a07f65701230c1006e260fa Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 16:11:34 +1030 Subject: [PATCH 47/85] Adding private message sound capability --- chat/lib/lang/fi.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/lib/lang/fi.php b/chat/lib/lang/fi.php index fc215bb..8d63a31 100644 --- a/chat/lib/lang/fi.php +++ b/chat/lib/lang/fi.php @@ -106,7 +106,7 @@ $lang['settingsSoundLeave'] = 'Ääni poistumiseen keskustelusta ja kanavalta:'; $lang['settingsSoundChatBot'] = 'Ääni chatbotin viesteille:'; $lang['settingsSoundError'] = 'Ääni virheilmoituksille:'; -$lang['settingsSoundPrivate'] = 'Ääni private viesteille:'; +$lang['settingsSoundPrivate'] = 'Ääni varten yksityiset viesteille:'; $lang['settingsBlink'] = 'Vilkuta ikkunan nimeä uusista viesteistä:'; $lang['settingsBlinkInterval'] = 'Vilkuttamisen aika millisekunneissa:'; $lang['settingsBlinkIntervalNumber'] = 'Vilkutuksen viive:'; From 50053d173a6392edf907f4bea36174d1bd14bc06 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 16:26:54 +1030 Subject: [PATCH 48/85] Adding private message sound capability --- chat/lib/lang/uk.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat/lib/lang/uk.php b/chat/lib/lang/uk.php index ff49519..28b4a8f 100644 --- a/chat/lib/lang/uk.php +++ b/chat/lib/lang/uk.php @@ -104,8 +104,8 @@ $lang['settingsSoundEnter'] = 'Звук для події входу в Чат чи кімнату:'; $lang['settingsSoundLeave'] = 'Звук для події виходу з Чату чи кімнати:'; $lang['settingsSoundChatBot'] = 'Звук для системних повідомлень:'; -$lang['settingsSoundError'] = 'Звук для особистих повідомлень:'; -$lang['settingsSoundPrivate'] = 'Sound for private messages:'; +$lang['settingsSoundError'] = 'Звук для помилок:'; +$lang['settingsSoundPrivate'] = 'Звук для особистих повідомлень:'; $lang['settingsBlink'] = 'Сигналізувати при появі нових повідомлень (блимання заголовку вікна):'; $lang['settingsBlinkInterval'] = 'Тривалість блимання заголовку вікна:'; $lang['settingsBlinkIntervalNumber'] = 'Кількість блимань:'; From 675b8db16ce6b9876c3fea00ffa064b223f5af68 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 16:39:06 +1030 Subject: [PATCH 49/85] Adding private message sound capability --- chat/lib/lang/da.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/lib/lang/da.php b/chat/lib/lang/da.php index ad5a453..224da8e 100644 --- a/chat/lib/lang/da.php +++ b/chat/lib/lang/da.php @@ -104,7 +104,7 @@ $lang['settingsSoundLeave'] = 'Lyd ved login og forlad kanal beskeder:'; $lang['settingsSoundChatBot'] = 'Lyd ved ChatBot beskeder:'; $lang['settingsSoundError'] = 'Lyd ved fejlmeddelse:'; -$lang['settingsSoundPrivate'] = 'Lyd ved private beskeder:'; +$lang['settingsSoundPrivate'] = 'Lyd ved privat beskeder:'; $lang['settingsBlink'] = 'Blink vindue titel ved nye beskeder:'; $lang['settingsBlinkInterval'] = 'Blink interval i millisekonder:'; $lang['settingsBlinkIntervalNumber'] = 'Antal blink intervaler:'; From 46ed1eb138b80f61eb16c8730d7927f2167be2c1 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 17:56:31 +1030 Subject: [PATCH 50/85] Adding private message sound capability By default, the new sound_7 soundFile uses the existing sound_1.mp3 file. To have an actual unique sound for private messaging an extra sound clip will be needed the sound_7 sound file location updated - currently line 214 of config.js. --- chat/js/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/js/config.js b/chat/js/config.js index d1863d7..40d5c06 100644 --- a/chat/js/config.js +++ b/chat/js/config.js @@ -211,7 +211,7 @@ var ajaxChatConfig = { sound_4: 'sound_4.mp3', sound_5: 'sound_5.mp3', sound_6: 'sound_6.mp3', - sound_7: 'sound_5.mp3' + sound_7: 'sound_1.mp3' }, From fa9c35f5f5e6681ff15a7bbdac524cb18fce97f7 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 18:44:27 +1030 Subject: [PATCH 51/85] Adding private message sound capability --- chat/lib/lang/et.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/lib/lang/et.php b/chat/lib/lang/et.php index 1c82fe1..173e8fa 100644 --- a/chat/lib/lang/et.php +++ b/chat/lib/lang/et.php @@ -104,7 +104,7 @@ $lang['settingsSoundLeave'] = 'Heli väljumise ja kanalitest lahkumise sõnumitel:'; $lang['settingsSoundChatBot'] = 'Chatboti sõnumite heli:'; $lang['settingsSoundError'] = 'Veateate heli:'; -$lang['settingsSoundPrivate'] = 'Privaatsõnum heli:'; +$lang['settingsSoundPrivate'] = 'Privaatsõnum sõnumite heli:'; $lang['settingsBlink'] = 'Vilguta akna tiitlit uute sõnumite saabumisel:'; $lang['settingsBlinkInterval'] = 'Vilgutamise intervall millisekundites:'; $lang['settingsBlinkIntervalNumber'] = 'Vilksatuste arv intervallis:'; From 80a7ce00ca72da224c4697947ddf6d76c64f64b9 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 16 Jan 2014 18:47:48 +1030 Subject: [PATCH 52/85] Adding private message sound capability --- chat/lib/lang/pt-pt.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chat/lib/lang/pt-pt.php b/chat/lib/lang/pt-pt.php index 90e70ee..d9f5cd6 100644 --- a/chat/lib/lang/pt-pt.php +++ b/chat/lib/lang/pt-pt.php @@ -105,7 +105,7 @@ $lang['settingsSoundLeave'] = 'Som de logout:'; $lang['settingsSoundChatBot'] = 'Som para mensagens do chatbot:'; $lang['settingsSoundError'] = 'Som para mensagens de erro:'; -$lang['settingsSoundPrivate'] = 'Som para mensagens de private:'; +$lang['settingsSoundPrivate'] = 'Som para mensagens de privadas:'; $lang['settingsBlink'] = 'Título da janela do "piscamento" em mensagens novas:'; $lang['settingsBlinkInterval'] = 'Intervalo do "piscamento" em milissegundos:'; $lang['settingsBlinkIntervalNumber'] = 'Número de intervalos do "piscamento":'; From b4cea6569e07a95fe15c0887d7e751595d410e47 Mon Sep 17 00:00:00 2001 From: Frug Date: Wed, 22 Jan 2014 17:01:49 -0500 Subject: [PATCH 53/85] cleanup --- chat/js/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index ebc6c54..b5165dd 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -695,10 +695,10 @@ var ajaxChat = { switch(messageParts[0]) { case '/privmsg': this.playSound(this.settings['soundPrivate']); - break; + break; default: this.playSound(this.settings['soundReceive']); - }; + } break; } } From 5897a3bc228db59a1ebdbc366a42ebd79b7bf3ca Mon Sep 17 00:00:00 2001 From: Sophist Date: Fri, 24 Jan 2014 17:51:44 +0000 Subject: [PATCH 54/85] Tweak alignment, add custom.css capability --- chat/css/Cobalt.css | 1 + chat/css/Core.css | 1 + chat/css/Lithium.css | 1 + chat/css/Mercury.css | 1 + chat/css/MyBB.css | 1 + chat/css/Oxygen.css | 1 + chat/css/Plum.css | 1 + chat/css/Sulfur.css | 1 + chat/css/Uranium.css | 1 + chat/css/beige.css | 1 + chat/css/black.css | 1 + chat/css/custom.css | 36 ++++++++++++++++++++++++++++++++++++ chat/css/global.css | 11 ++++++----- chat/css/grey.css | 1 + chat/css/prosilver.css | 1 + chat/css/subSilver.css | 1 + chat/css/subblack2.css | 1 + chat/css/vBulletin.css | 1 + 18 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 chat/css/custom.css diff --git a/chat/css/Cobalt.css b/chat/css/Cobalt.css index 08c4e36..5a87c01 100644 --- a/chat/css/Cobalt.css +++ b/chat/css/Cobalt.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Core.css b/chat/css/Core.css index 104fb1b..d6f751b 100644 --- a/chat/css/Core.css +++ b/chat/css/Core.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Lithium.css b/chat/css/Lithium.css index 8776158..a9d5ed8 100644 --- a/chat/css/Lithium.css +++ b/chat/css/Lithium.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Mercury.css b/chat/css/Mercury.css index f7e12d5..38dff16 100644 --- a/chat/css/Mercury.css +++ b/chat/css/Mercury.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/MyBB.css b/chat/css/MyBB.css index 9daa19d..8220875 100644 --- a/chat/css/MyBB.css +++ b/chat/css/MyBB.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Oxygen.css b/chat/css/Oxygen.css index 9163174..b64a869 100644 --- a/chat/css/Oxygen.css +++ b/chat/css/Oxygen.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Plum.css b/chat/css/Plum.css index a1aec9f..2b8c304 100644 --- a/chat/css/Plum.css +++ b/chat/css/Plum.css @@ -9,6 +9,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Sulfur.css b/chat/css/Sulfur.css index f7d721c..384141a 100644 --- a/chat/css/Sulfur.css +++ b/chat/css/Sulfur.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/Uranium.css b/chat/css/Uranium.css index 7ff9b64..062c970 100644 --- a/chat/css/Uranium.css +++ b/chat/css/Uranium.css @@ -10,6 +10,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/beige.css b/chat/css/beige.css index 7c30bbe..19f4771 100644 --- a/chat/css/beige.css +++ b/chat/css/beige.css @@ -10,6 +10,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/black.css b/chat/css/black.css index 292b5d1..7a9d12d 100644 --- a/chat/css/black.css +++ b/chat/css/black.css @@ -10,6 +10,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/custom.css b/chat/css/custom.css new file mode 100644 index 0000000..ab4a1db --- /dev/null +++ b/chat/css/custom.css @@ -0,0 +1,36 @@ +/* + * @package AJAX_Chat + * @author Sebastian Tschan + * @author Philip Nicolcev + * @copyright (c) Sebastian Tschan + * @license Modified MIT License + * @link https://blueimp.net/ajax/ + */ + +/* Custom CSS - enter css overrides for all styles below */ +/* e.g. suppose you want to hide the logout button and the channel / style / language selectors +#content #logoutChannelContainer { + display:none; +} +#content #chatList { + top:66px; +} +#content #onlineListContainer { + top:66px; +} +#content #copyright { + right:50px; + top:18px; +} +#content #statusIconContainer { + right:12px; + top:12px; +} +#content #helpContainer { + top:66px; +} +#content #settingsContainer { + top:66px; +} + + */ diff --git a/chat/css/global.css b/chat/css/global.css index eff916f..dbfa02d 100644 --- a/chat/css/global.css +++ b/chat/css/global.css @@ -36,7 +36,7 @@ #content #copyright { position:absolute; right:20px; - top:20px; + top:18px; } #content #headlineContainer { position:absolute; @@ -71,13 +71,13 @@ position:absolute; left:20px; right:20px; - bottom:95px; + bottom:90px; padding-right:4px; } #content #submitButtonContainer { position:absolute; right:20px; - bottom:60px; + bottom:54px; } #content #onlineListContainer { position:absolute; @@ -105,6 +105,7 @@ left:20px; bottom:20px; padding:3px; + padding-left: 0px; } #content #colorCodesContainer { position:absolute; @@ -122,7 +123,7 @@ #content #optionsContainer { position:absolute; right:20px; - bottom:20px; + bottom:24px; padding:3px; padding-right:0px; } @@ -259,7 +260,7 @@ #content img { border:none; } -#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #colorCodesContainer a, #content textarea { border-width:1px; border-style:solid; diff --git a/chat/css/grey.css b/chat/css/grey.css index 36d55d6..7d58380 100644 --- a/chat/css/grey.css +++ b/chat/css/grey.css @@ -11,6 +11,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/prosilver.css b/chat/css/prosilver.css index 867c30d..76ba6dd 100644 --- a/chat/css/prosilver.css +++ b/chat/css/prosilver.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/subSilver.css b/chat/css/subSilver.css index 80f8b75..f105b41 100644 --- a/chat/css/subSilver.css +++ b/chat/css/subSilver.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/subblack2.css b/chat/css/subblack2.css index 3043754..c1cb0db 100644 --- a/chat/css/subblack2.css +++ b/chat/css/subblack2.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { diff --git a/chat/css/vBulletin.css b/chat/css/vBulletin.css index 91a7e1a..b8816c2 100644 --- a/chat/css/vBulletin.css +++ b/chat/css/vBulletin.css @@ -13,6 +13,7 @@ @import url('global.css'); @import url('fonts.css'); @import url('print.css'); +@import url('custom.css'); @media screen,projection,handheld { From 85cfe0177441b2d67c274479e052421713097d30 Mon Sep 17 00:00:00 2001 From: Frug Date: Sat, 28 Jun 2014 18:03:52 -0400 Subject: [PATCH 55/85] clear DOM buffer before reporting failed xml syntax. This is the cause of the img tag crash exploit when putting newlines in an img tag --- chat/js/chat.js | 1 + 1 file changed, 1 insertion(+) diff --git a/chat/js/chat.js b/chat/js/chat.js index b5165dd..34d8514 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -355,6 +355,7 @@ var ajaxChat = { domNode.innerHTML += str; } } catch(e) { + this.DOMbuffer = ''; this.addChatBotMessageToChatList('/error DOMSyntax '+id); this.updateChatlistView(); } From 803edcdc34992bdbe19b2c4bafc0a565d667c3c7 Mon Sep 17 00:00:00 2001 From: Frug Date: Sun, 29 Jun 2014 12:05:38 -0400 Subject: [PATCH 56/85] Strip all tags from [img] urls --- chat/js/chat.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index 34d8514..0c2d2db 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -2553,7 +2553,7 @@ var ajaxChat = { // Avoid invalid XHTML (unclosed tags): if(ajaxChat.containsUnclosedTags(p3)) { return str; - } + } switch(p1) { case 'color': return ajaxChat.replaceBBCodeColor(p3, p2); @@ -2613,7 +2613,7 @@ var ajaxChat = { ); if(!url || !url.match(regExpUrl)) return url; - url = url.replace(/\s/gm, this.encodeText(' ')); + url = this.stripTags(url.replace(/\s/gm, this.encodeText(' '))); maxWidth = this.dom['chatList'].offsetWidth-50; maxHeight = this.dom['chatList'].offsetHeight-50; return ''; + +'" alt="" onload="ajaxChat.updateChatlistView();" onerror="this.src=\'img/broken-image.png\'"/>'; } return url; }, From fb2024c2eba72ed7f21524733947e4631f1b9639 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 10:42:26 -0400 Subject: [PATCH 58/85] Scroll on a 50ms delay to hopefully solve some autoscroll browser issues --- chat/js/chat.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index 3e5caeb..0fbd5f1 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -1408,9 +1408,14 @@ var ajaxChat = { } if(this.settings['autoScroll']) { - this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight; + var self = this; + setTimeout(function() { self.scrollChatList(); }, 50); } }, + + scrollChatList: function() { + this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight; + }, encodeText: function(text) { return encodeURIComponent(text); From 2b94a84c38f1cc920ec32e168ad5668e2f64a625 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 13:12:07 -0400 Subject: [PATCH 59/85] new readme file --- chat/readme.html | 259 ++++++++++++++++++++++++++--------------------- 1 file changed, 146 insertions(+), 113 deletions(-) diff --git a/chat/readme.html b/chat/readme.html index 145e188..e6137ab 100644 --- a/chat/readme.html +++ b/chat/readme.html @@ -6,128 +6,146 @@ -
+
+
+

+ AJAX Chat + + v 0.8.8 standalone ( blueimp.net/ajax/ ) + +

+
+
-

AJAX Chat - - v 0.8.7 standalone ( blueimp.net/ajax/ ) - -

+
-

This is the standalone version of blueimp's AJAX Chat designed to run on its own, without another web application.
-If you want to integrate AJAX Chat with one of the forums we support, go back and choose the right version.
-This version is good for customizing your own integration, or using on its own.

+
+

Version Information

+
+ This is the standalone version of blueimp's AJAX Chat designed to run on its own, without another web application.
+ If you want to integrate AJAX Chat with one of the forums we support, go back and choose the right version.
+ This version is good for customizing your own integration, or using on its own.

-

- AJAX stands for "Asynchronous JavaScript and XML".
- The AJAX Chat client (your browser) uses JavaScript to query the web server for updates.
- Instead of delivering a complete HTML page only updated data is sent in XML format.
- By using JavaScript the chat page can be updated without having to reload the whole page.
- PHP is used to communicate with the database and authenticate users. -

+

+ AJAX stands for "Asynchronous JavaScript and XML".
+ The AJAX Chat client (your browser) uses JavaScript to query the web server for updates.
+ Instead of delivering a complete HTML page only updated data is sent in XML format.
+ By using JavaScript the chat page can be updated without having to reload the whole page.
+ PHP is used to communicate with the database and authenticate users. +

+
+
-

Requirements

+

1. Requirements

- - - - - - - - - -
Server-SideClient-Side
- PHP >= 5
- MySQL >= 4
- Ruby >= 1.8 (optional) -
- Enabled JavaScript
- Enabled Cookies
- Flash Plugin >= 9 (optional) -
+ + + + + + + + + +
Server-SideClient-Side
+ PHP >= 5
+ MySQL >= 4
+ Ruby >= 1.8 (optional) +
+ Enabled JavaScript
+ Enabled Cookies
+ Flash Plugin >= 9 (optional) +
-

Installation

+

2. Installation

Download your preferred version of AJAX Chat and unzip the file on your computer.

-

Before You Begin

-
-

- In order to edit PHP files you will need a good text editor. You should not use Windows notepad, wordpad, or Microsoft Word to edit PHP files. These programs will add something called a byte-order-mark (BOM) to the files and this may prevent chat from functioning properly. - We recommend using Notepad ++ ( http://notepad-plus-plus.org ) for editing all files. It also has the benefit of color-coding your files so you can edit them more easily.
- If you get an error message like "Cannot modify header information - headers already sent" it is likely because you have used one of the above programs to edit files. -

-
+ +
+

Use a Proper Text Editor!

+ In order to edit PHP files you will need a good text editor. You should not use Windows notepad, wordpad, or Microsoft Word to edit PHP files. These programs will add something called a byte-order-mark (BOM) to the files and this may prevent chat from functioning properly. + We recommend using Notepad ++ ( http://notepad-plus-plus.org ) for editing all files. It also has the benefit of color-coding your files so you can edit them more easily.
+ If you get an error message like "Cannot modify header information - headers already sent" it is likely because you have used one of the above programs to edit files. +

Configure Database Settings

-

- The first and most important thing you need to do is tell AJAX Chat how to connect to your database. This, and all core settings must be located inside the file lib/config.php.
- You need to create this file.
- An example config.php file can be found in lib/config.php.example that shipped with chat.
- Duplicate this file and save it as config.php (or just delete .example from the end of the file name) and then fill out at least the following four fields in the file:

-

- $config['dbConnection']['host'] = 'your_database_hostname';
- $config['dbConnection']['user'] = 'your_database_username';
- $config['dbConnection']['pass'] = 'your_database_password';
- $config['dbConnection']['name'] = 'your_database_name';
-

-

Sufficed to say you need this information. Talk to your hosting provider if you don't know.

-

In most cases, chat will function with only these fields filled out and you can proceed to the next step.
-

-

If your host does not use mysqli you will need to change the connection type field:
- $config['dbConnection']['type'] = null;
- If this is set to "null" it defaults to "mysqli" if existing, else to "mysql". In most cases this field can be left as null.
-
- You can reference an existing database connection link or object by changing:
- $config['dbConnection']['link'] = null;
- If this is set to null, a new database connection is created.

+

+ The first and most important thing you need to do is tell AJAX Chat how to connect to your database. This, and all core settings must be located inside the file lib/config.php.
+ You need to create this file.
+ An example config.php file can be found in lib/config.php.example that shipped with chat.
+ Duplicate this file and save it as config.php (or just delete .example from the end of the file name) and then fill out at least the following four fields in the file: +

+

+ + $config['dbConnection']['host'] = 'your_database_hostname';
+ $config['dbConnection']['user'] = 'your_database_username';
+ $config['dbConnection']['pass'] = 'your_database_password';
+ $config['dbConnection']['name'] = 'your_database_name'; +
+

+

Sufficed to say you need this information. Talk to your hosting provider if you don't know.

+

+ In most cases, chat will function with only these fields filled out and you can proceed to the next step.
+

+

+ If your host does not use mysqli you will need to change the connection type field:
+ $config['dbConnection']['type'] = null;
+ If this is set to "null" it defaults to "mysqli" if existing, else to "mysql". In most cases this field can be left as null.
+
+ You can reference an existing database connection link or object by changing:
+ $config['dbConnection']['link'] = null;
+ If this is set to null, a new database connection is created. +

Choose Your Channel Settings

@@ -143,19 +161,25 @@

Choose Your Channel Settings

Add Your Users

-

Edit users in lib/data/users.php.
- Users follow the following format: -

-

$users[user id] = array();
- $users[user id]['userRole'] = AJAX_CHAT_ROLE;
- $users[user id]['userName'] = 'user name';
- $users[user id]['password'] = 'user password';
- $users[user id]['channels'] = array(allowed channel ids);

- Each user must have a unique user id number and a unique name.
- The first user in the list (user id 0) is used for the guest user settings. All guest users will have access to the channels set for this user and the user role AJAX_CHAT_GUEST.
- Registered users can have the user roles AJAX_CHAT_USER, AJAX_CHAT_MODERATOR or AJAX_CHAT_ADMIN. (this is case sensitive, type it exactly)
- The list of channels a user has access to can be set for each user individually. Channel id's are separated by commas. eg: array(0,1,23); allows channels 0, 1 and 23.
- Whitespace in the user names will be converted to the underscore "_".

+

+ The Standalone version of chat uses a php file to store users and rooms while the database is used for chat messages, invites and bans.
+ The integration versions typically make use of a database for users and rooms. If you desire a way to manage users without having to edit a php file, consider using an integration version. +

+

+ Edit users in lib/data/users.php.
+ Users follow the following format: +

+

$users[user id] = array();
+ $users[user id]['userRole'] = AJAX_CHAT_ROLE;
+ $users[user id]['userName'] = 'user name';
+ $users[user id]['password'] = 'user password';
+ $users[user id]['channels'] = array(allowed channel ids);

+ Each user must have a unique user id number and a unique name.
+ The first user in the list (user id 0) is used for the guest user settings. All guest users will have access to the channels set for this user and the user role AJAX_CHAT_GUEST.
+ Registered users can have the user roles AJAX_CHAT_USER, AJAX_CHAT_MODERATOR or AJAX_CHAT_ADMIN. (this is case sensitive, type it exactly)
+ The list of channels a user has access to can be set for each user individually. Channel id's are separated by commas. eg: array(0,1,23); allows channels 0, 1 and 23.
+ Whitespace in the user names will be converted to the underscore "_". +

Upload to Your Server

@@ -167,10 +191,13 @@

Create the Database Tables

There are two options available to you to create the database. The first, and usually the easiest option, is to run the installation script included with AJAX Chat. Alternatively, you may use a database tool like PHPMyAdmin to manually create the tables.

    -
  1. To use the installation script, visit the following URL in your browser:
    - http://example.org/path/to/chat/install.php
    - Where - "http://example.org/path/to/chat/" is the real URL to your chat directory.
  2. +
  3. + To use the installation script, visit the following URL in your browser:
    + http://example.org/path/to/chat/install.php
    + Where + "http://example.org/path/to/chat/" is the real URL to your chat directory.
    + Be sure to delete the install.php file after you have completed this step! +
  4. To install it manually using PHPMyAdmin or a similar tool, copy the contents of the chat.sql file and run it as a query.

Either of these methods will create the tables your database needs to store chat messages and other information.

@@ -188,7 +215,7 @@

Congradulation! You Are Winner!

-

Configuring and Customizing

+

3. Configuring and Customizing

Configuration Files

@@ -259,7 +286,7 @@

Adding Features

-

Logs

+

4. Logs

Accessing the Logs

@@ -275,7 +302,7 @@

Accessing the Logs

-

Shoutbox

+

5. Shoutbox

AJAX Chat is also usable as shoutbox - this is a short guide on how to set it up:

@@ -332,8 +359,15 @@

Shoutbox Output

-

Socket Server

+

6. Socket Server

+

+ This part of the setup is OPTIONAL and meant for experienced users only.
+ The Socket Server is no longer actively supported and may not function correctly out of the box. + Later versions of AJAX Chat may have this socket server implementation replaced with something else.
+ Please do not report bugs regarding the socket server - you're on your own with this one! +

+

Using the AJAX technology alone the chat clients have to permanently pull updates from the server.
This is due to AJAX being a web technology and HTTP being a stateless protocol.
Events pushed from server-side need a permanent or long-lasting socket connection between clients and server.
@@ -341,7 +375,6 @@

Socket Server

AJAX Chat uses a JavaScript-to-Flash bridge to establish a permanent socket connection from client side.
The JavaScript-to-Flash bridge requires a Flash plugin >= 9 installed on the user browser.
Clients without this requirement will fall back to pull the server for updates.

-

This part of the setup is OPTIONAL and meant for experienced users only.

Installation

The socket server coming with AJAX Chat is implemented in Ruby.
@@ -399,7 +432,7 @@

Flash Permissions

 

-

Support

+

7. Support

Please do not email the devs with support questions.
@@ -409,7 +442,7 @@

Support

- +

Your donations contribute to the growth and development of this project and are always appreciated.
@@ -423,7 +456,7 @@

-

License

+

9. License

Bluimp's AJAX Chat is released under a Modified MIT License.

You should also find this license included with your download of this project.

@@ -433,4 +466,4 @@

License

- + \ No newline at end of file From 89cc6ba3ff3487265b34a833340b8e9d8fd5cb26 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 13:24:55 -0400 Subject: [PATCH 60/85] invalid html --- chat/readme.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat/readme.html b/chat/readme.html index e6137ab..95f7340 100644 --- a/chat/readme.html +++ b/chat/readme.html @@ -72,7 +72,7 @@

Version Information

This is the standalone version of blueimp's AJAX Chat designed to run on its own, without another web application.
If you want to integrate AJAX Chat with one of the forums we support, go back and choose the right version.
- This version is good for customizing your own integration, or using on its own.

+ This version is good for customizing your own integration, or using on its own.

AJAX stands for "Asynchronous JavaScript and XML".
@@ -449,7 +449,7 @@

- +
I'm on gittip at https://www.gittip.com/Frug From a815e10c6ec34cacc52f59aa765d2b54a510b55b Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 13:41:17 -0400 Subject: [PATCH 61/85] section for 0.8.8 --- chat/changelog.txt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/chat/changelog.txt b/chat/changelog.txt index 733eb99..97ea565 100644 --- a/chat/changelog.txt +++ b/chat/changelog.txt @@ -843,4 +843,15 @@ Bugfixes: - Fix potential conflict between php and mysql timezones by using mysql's FROM_UNIXTIME(). - thanks to ManOnDaMoon. - Normalized sound volumes a bit. - Fixed mybb integration database connection. Connection details should be pulled automatically now. -- Fidex mybb integration guest logins to accept guests that don't enter a username (assign numbers like other versions). \ No newline at end of file +- Fidex mybb integration guest logins to accept guests that don't enter a username (assign numbers like other versions). + +Version 0.8.8 (?.?.2014) +------------------------------- +Notice: +- If you are using the javascript override handleCustomInfoMessage in custom.js, you should make it return true if you capture an event + +New Features: + + +Bugfixes: +- HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to \ No newline at end of file From 04677819f7377ba083d5fdd530f8c1f4cd413507 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 13:52:27 -0400 Subject: [PATCH 62/85] handleCustomInfoMessage can override default handleInfoMessage by returning true --- chat/js/chat.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index 0fbd5f1..0c42c96 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -890,6 +890,9 @@ var ajaxChat = { }, handleInfoMessage: function(infoType, infoData) { + if (this.handleCustomInfoMessage(infoType, infoData) === true) { + return; + } switch(infoType) { case 'channelSwitch': this.clearChatList(); @@ -923,7 +926,7 @@ var ajaxChat = { this.socketRegistrationID = infoData; this.socketRegister(); default: - this.handleCustomInfoMessage(infoType, infoData); + return; } }, From 05c79baa6489b9225d118212675cad4d0807b4fe Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 15:49:26 -0400 Subject: [PATCH 63/85] use custom PM sound (sound-7) in config --- chat/js/chat.js | 13 ++++---- chat/js/config.js | 67 ++++++++++++++++------------------------ chat/sounds/sound_7.mp3 | Bin 0 -> 24241 bytes chat/sounds/sound_8.mp3 | Bin 0 -> 33436 bytes 4 files changed, 34 insertions(+), 46 deletions(-) create mode 100644 chat/sounds/sound_7.mp3 create mode 100644 chat/sounds/sound_8.mp3 diff --git a/chat/js/chat.js b/chat/js/chat.js index 0c42c96..1c7c165 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -668,6 +668,7 @@ var ajaxChat = { playSoundOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { if(this.settings['audio'] && this.sounds && this.lastID && !this.channelSwitch) { + console.log(this.settings['soundPrivate']); switch(userID) { case this.chatBotID: var messageParts = messageText.split(' ', 1); @@ -900,7 +901,7 @@ var ajaxChat = { this.setSelectedChannel(infoData); this.channelName = infoData; this.channelSwitch = true; - break; + break; case 'channelName': this.setSelectedChannel(infoData); this.channelName = infoData; @@ -910,7 +911,7 @@ var ajaxChat = { break; case 'userID': this.userID = infoData; - break; + break; case 'userName': this.userName = infoData; this.encodedUserName = this.scriptLinkEncode(this.userName); @@ -918,7 +919,7 @@ var ajaxChat = { break; case 'userRole': this.userRole = infoData; - break; + break; case 'logout': this.handleLogout(infoData); return; @@ -960,9 +961,9 @@ var ajaxChat = { if(!this.inArray(onlineUsers, this.usersList[i])) { this.removeUserFromOnlineList(this.usersList[i], i); } - } - this.setOnlineListRowClasses(); - } + } + this.setOnlineListRowClasses(); + } }, handleChatMessages: function(messageNodes) { diff --git a/chat/js/config.js b/chat/js/config.js index 40d5c06..2f8c0d7 100644 --- a/chat/js/config.js +++ b/chat/js/config.js @@ -1,9 +1,5 @@ /* - * @package AJAX_Chat - * @author Sebastian Tschan - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ + * AJAX Chat client-side configuration */ // Ajax Chat config parameters: @@ -12,11 +8,11 @@ var ajaxChatConfig = { // The channelID of the channel to enter on login (the loginChannelName is used if set to null): loginChannelID: null, // The channelName of the channel to enter on login (the default channel is used if set to null): - loginChannelName: null, - + loginChannelName: null, + // The time in ms between update calls to retrieve new chat messages: timerRate: 2000, - + // The URL to retrieve the XML chat messages (must at least contain one parameter): ajaxURL: './?ajax=true', // The base URL of the chat directory, used to retrieve media files (images, sound files, etc.): @@ -24,10 +20,10 @@ var ajaxChatConfig = { // A regular expression for allowed source URL's for media content (e.g. images displayed inline); regExpMediaUrl: '^((http)|(https)):\\/\\/', - + // If set to false the chat update is delayed until the event defined in ajaxChat.setStartChatHandler(): startChatOnLoad: true, - + // Defines the IDs of DOM nodes accessed by the chat: domIDs: { // The ID of the chat messages list: @@ -64,27 +60,27 @@ var ajaxChatConfig = { lineBreaks: true, // Defines if emoticon codes are replaced with their associated images: emoticons: true, - + // Defines if the focus is automatically set to the input field on chat load or channel switch: autoFocus: true, // Defines if the chat list scrolls automatically to display the latest messages: - autoScroll: true, + autoScroll: true, // The maximum count of messages displayed in the chat list (will be ignored if set to 0): maxMessages: 0, - + // Defines if long words are wrapped to avoid vertical scrolling: wordWrap: true, - // Defines the maximum length before a word gets wrapped: + // Defines the maximum length before a word gets wrapped: maxWordLength: 32, - + // Defines the format of the date and time displayed for each chat message: dateFormat: '(%H:%i:%s)', - + // Defines if font colors persist without the need to assign them to each message: - persistFontColor: false, + persistFontColor: false, // The default font color, uses the page default font color if set to null: fontColor: null, - + // Defines if sounds are played: audio: true, // Defines the sound volume (0.0 = mute, 1.0 = max): @@ -103,8 +99,8 @@ var ajaxChatConfig = { // Defines the sound that is played on error messages: soundError: 'sound_6', // Defines the sound that is played when private messages are received: - soundPrivate: 'sound_7', - + soundPrivate: 'sound_7', + // Defines if the document title blinks on new messages: blink: true, // Defines the blink interval in ms: @@ -112,7 +108,7 @@ var ajaxChatConfig = { // Defines the number of blink intervals: blinkIntervalNumber: 10 }, - + // Defines a list of settings which are not to be stored in a session cookie: nonPersistentSettings: [], @@ -127,12 +123,12 @@ var ajaxChatConfig = { 'url', 'img' ], - + // Defines the list of allowed color codes: colorCodes: [ 'gray', 'silver', - 'white', + 'white', 'yellow', 'orange', 'red', @@ -148,7 +144,7 @@ var ajaxChatConfig = { 'maroon', 'black' ], - + // Defines the list of allowed emoticon codes: emoticonCodes: [ ':)', @@ -173,9 +169,9 @@ var ajaxChatConfig = { ':help:', ':error:', ':warning:', - ':favorite:' + ':favorite:' ], - + // Defines the list of emoticon files associated with the emoticon codes: emoticonFiles: [ 'smile.png', @@ -211,16 +207,15 @@ var ajaxChatConfig = { sound_4: 'sound_4.mp3', sound_5: 'sound_5.mp3', sound_6: 'sound_6.mp3', - sound_7: 'sound_1.mp3' + sound_7: 'sound_7.mp3' }, - - + + // Once users have been logged in, the following values are overridden by those in config.php. // You should set these to be the same as the ones in config.php to avoid confusion. - + // Session identification, used for style and setting cookies: sessionName: 'ajax_chat', - // The time in days until the style and setting cookies expire: cookieExpiration: 365, // The path of the cookies, '/' allows to read the cookies from all directories: @@ -229,29 +224,22 @@ var ajaxChatConfig = { cookieDomain: null, // If enabled, cookies must be sent over secure (SSL/TLS encrypted) connections: cookieSecure: null, - // The name of the chat bot: chatBotName: 'ChatBot', // The userID of the chat bot: chatBotID: 2147483647, - // Allow/Disallow registered users to delete their own messages: allowUserMessageDelete: true, - // Minutes until a user is declared inactive (last status update) - the minimum is 2 minutes: inactiveTimeout: 2, - // UserID plus this value are private channels (this is also the max userID and max channelID): privateChannelDiff: 500000000, // UserID plus this value are used for private messages: privateMessageDiff: 1000000000, - // Defines if login/logout and channel enter/leave are displayed: showChannelMessages: true, - // Max messageText length: messageTextMaxLength: 1040, - // Defines if the socket server is enabled: socketServerEnabled: false, // Defines the hostname of the socket server used to connect from client side: @@ -260,5 +248,4 @@ var ajaxChatConfig = { socketServerPort: 1935, // This ID can be used to distinguish between different chat installations using the same socket server: socketServerChatID: 0 - -} +}; \ No newline at end of file diff --git a/chat/sounds/sound_7.mp3 b/chat/sounds/sound_7.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7ee9273e4001c30e5af30c687a6b7d97fa29e299 GIT binary patch literal 24241 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
'; }, - + getChatListUserNameTitle: function(userID, userName, userRole, ip) { - return (ip !== null) ? ' title="IP: ' + ip + '"' : ''; + return (ip !== null) ? ' title="IP: ' + ip + '"' : ''; }, - + getMessageDocumentID: function(messageID) { return ((messageID === null) ? 'ajaxChat_lm_'+(this.localID++) : 'ajaxChat_m_'+messageID); }, - + getMessageNode: function(messageID) { return ((messageID === null) ? null : document.getElementById(this.getMessageDocumentID(messageID))); }, - + getUserDocumentID: function(userID) { return 'ajaxChat_u_'+userID; }, - + getUserNode: function(userID) { return document.getElementById(this.getUserDocumentID(userID)); }, @@ -1310,11 +1316,11 @@ var ajaxChat = { getUserMenuDocumentID: function(userID) { return 'ajaxChat_um_'+userID; }, - + getInlineUserMenuDocumentID: function(menuID, index) { return 'ajaxChat_ium_'+menuID+'_'+index; }, - + getDeletionLink: function(messageID, userID, userRole, channelID) { if(messageID !== null && this.isAllowedToDeleteMessage(messageID, userID, userRole, channelID)) { if(!arguments.callee.deleteMessage) { @@ -1328,7 +1334,7 @@ var ajaxChat = { } return ''; }, - + isAllowedToDeleteMessage: function(messageID, userID, userRole, channelID) { if((((this.userRole === '1' && this.allowUserMessageDelete && (userID === this.userID || parseInt(channelID) === parseInt(this.userID)+this.privateMessageDiff || @@ -1338,7 +1344,7 @@ var ajaxChat = { } return false; }, - + onNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { if(!this.customOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) { return false; @@ -1372,7 +1378,7 @@ var ajaxChat = { } return false; }, - + blinkOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { if(this.settings['blink'] && this.lastID && !this.channelSwitch && userID !== this.userID) { clearInterval(this.blinkInterval); @@ -1382,7 +1388,7 @@ var ajaxChat = { ); } }, - + blinkUpdate: function(blinkStr) { if(!this.originalDocumentTitle) { this.originalDocumentTitle = document.title; @@ -1403,14 +1409,14 @@ var ajaxChat = { arguments.callee.blink++; } }, - - updateChatlistView: function() { + + updateChatlistView: function() { if(this.dom['chatList'].childNodes && this.settings['maxMessages']) { while(this.dom['chatList'].childNodes.length > this.settings['maxMessages']) { this.dom['chatList'].removeChild(this.dom['chatList'].firstChild); } } - + if(this.settings['autoScroll']) { var self = this; setTimeout(function() { self.scrollChatList(); }, 50); @@ -1420,7 +1426,7 @@ var ajaxChat = { scrollChatList: function() { this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight; }, - + encodeText: function(text) { return encodeURIComponent(text); }, @@ -1476,7 +1482,7 @@ var ajaxChat = { this.encodeSpecialCharsCallback ); }, - + encodeSpecialCharsCallback: function(str) { switch(str) { case '&': @@ -1497,13 +1503,13 @@ var ajaxChat = { decodeSpecialChars: function(text) { var regExp = new RegExp('(&)|(<)|(>)|(')|(")', 'g'); - + return text.replace( regExp, this.decodeSpecialCharsCallback ); }, - + decodeSpecialCharsCallback: function(str) { switch(str) { case '&': @@ -1520,7 +1526,7 @@ var ajaxChat = { return str; } }, - + inArray: function(haystack, needle) { var i = haystack.length; while(i--) { @@ -1545,7 +1551,7 @@ var ajaxChat = { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('<\\/?[^>]+?>', 'g'); } - + return str.replace(arguments.callee.regExp, ''); }, @@ -1553,9 +1559,9 @@ var ajaxChat = { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('\\[\\/?[^\\]]+?\\]', 'g'); } - + return str.replace(arguments.callee.regExp, ''); - }, + }, escapeRegExp: function(text) { if (!arguments.callee.regExp) { @@ -1569,7 +1575,7 @@ var ajaxChat = { } return text.replace(arguments.callee.regExp, '\\$1'); }, - + addSlashes: function(text) { // Adding slashes in front of apostrophs and backslashes to ensure a valid JavaScript expression: return text.replace(/\\/g, '\\\\').replace(/\'/g, '\\\''); @@ -1582,7 +1588,7 @@ var ajaxChat = { formatDate: function(format, date) { date = (date == null) ? new date() : date; - + return format .replace(/%Y/g, date.getFullYear()) .replace(/%m/g, this.addLeadingZero(date.getMonth()+1)) @@ -1591,7 +1597,7 @@ var ajaxChat = { .replace(/%i/g, this.addLeadingZero(date.getMinutes())) .replace(/%s/g, this.addLeadingZero(date.getSeconds())); }, - + addLeadingZero: function(number) { number = number.toString(); if(number.length < 2) { @@ -1632,7 +1638,7 @@ var ajaxChat = { return 'default'; } }, - + handleInputFieldKeyPress: function(event) { if(event.keyCode === 13 && !event.shiftKey) { this.sendMessage(); @@ -1649,7 +1655,7 @@ var ajaxChat = { handleInputFieldKeyUp: function(event) { this.updateMessageLengthCounter(); }, - + updateMessageLengthCounter: function() { if(this.dom['messageLengthCounter']) { this.updateDOM( @@ -1660,7 +1666,7 @@ var ajaxChat = { ); } }, - + sendMessage: function(text) { text = text ? text : this.dom['inputField'].value; if(!text) { @@ -1672,14 +1678,14 @@ var ajaxChat = { var message = 'lastID=' + this.lastID + '&text=' - + this.encodeText(text); + + this.encodeText(text); this.makeRequest(this.ajaxURL,'POST',message); } this.dom['inputField'].value = ''; this.dom['inputField'].focus(); this.updateMessageLengthCounter(); }, - + parseInputMessage: function(text) { var textParts; if(text.charAt(0) === '/') { @@ -1706,7 +1712,7 @@ var ajaxChat = { } return text; }, - + assignFontColorToMessage: function(text) { return '[color='+this.settings['fontColor']+']'+text+'[/color]'; }, @@ -1734,7 +1740,7 @@ var ajaxChat = { } return text; }, - + parseIgnoreInputCommand: function(text, textParts) { var userName, ignoredUserNames = this.getIgnoredUserNames(), i; if(textParts.length > 1) { @@ -1782,12 +1788,12 @@ var ajaxChat = { } return this.ignoredUserNames; }, - + setIgnoredUserNames: function(ignoredUserNames) { this.ignoredUserNames = ignoredUserNames; this.setSetting('ignoredUserNames', ignoredUserNames.join(' ')); }, - + ignoreMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { var textParts; if(userID === this.chatBotID && messageText.charAt(0) === '/') { @@ -1799,7 +1805,7 @@ var ajaxChat = { case '/roll': userName = textParts[1]; break; - } + } } } if(this.inArray(this.getIgnoredUserNames(), userName)) { @@ -1845,7 +1851,7 @@ var ajaxChat = { } } }, - + getClass: function(node) { if(typeof node.className !== 'undefined') { return node.className; // IE @@ -1853,7 +1859,7 @@ var ajaxChat = { return node.getAttribute('class'); } }, - + setClass: function(node, className) { if(typeof node.className !== 'undefined') { node.className = className; // IE @@ -1865,7 +1871,7 @@ var ajaxChat = { scriptLinkEncode: function(text) { return this.encodeText(this.addSlashes(this.decodeSpecialChars(text))); }, - + scriptLinkDecode: function(text) { return this.encodeSpecialChars(this.removeSlashes(this.decodeText(text))); }, @@ -1882,9 +1888,9 @@ var ajaxChat = { default: arguments.callee.utf8Decode = false; return value; - } + } } else if(arguments.callee.utf8Decode) { - return this.utf8Decode(value); + return this.utf8Decode(value); } else { return value; } @@ -1897,7 +1903,7 @@ var ajaxChat = { insertMessageWrapper: function(text) { this.insertText(this.getScriptLinkValue(text), true); }, - + switchChannel: function(channel) { if(!this.chatStarted) { this.clearChatList(); @@ -1907,11 +1913,11 @@ var ajaxChat = { this.requestTeaserContent(); return; } - clearTimeout(this.timer); + clearTimeout(this.timer); var message = 'lastID=' + this.lastID + '&channelName=' - + this.encodeText(channel); + + this.encodeText(channel); this.makeRequest(this.ajaxURL,'POST',message); if(this.dom['inputField'] && this.settings['autoFocus']) { this.dom['inputField'].focus(); @@ -1923,7 +1929,7 @@ var ajaxChat = { var message = 'logout=true'; this.makeRequest(this.ajaxURL,'POST',message); }, - + handleLogout: function(url) { window.location.href = url; }, @@ -1941,7 +1947,7 @@ var ajaxChat = { this.setClass(node, (this.getSetting(setting) ? 'button' : 'button off')); } }, - + showHide: function(id, styleDisplay, displayInline) { var node = document.getElementById(id); if(node) { @@ -1949,16 +1955,16 @@ var ajaxChat = { node.style.display = styleDisplay; } else { if(node.style.display === 'none') { - node.style.display = (displayInline ? 'inline' : 'block'); + node.style.display = (displayInline ? 'inline' : 'block'); } else { node.style.display = 'none'; } - } + } } }, setPersistFontColor: function(bool) { - this.settings['persistFontColor'] = bool; + this.settings['persistFontColor'] = bool; if(!this.settings['persistFontColor']) { this.settings['fontColor'] = null; if(this.dom['inputField']) { @@ -1983,16 +1989,16 @@ var ajaxChat = { this.insert('[color=' + color + ']', '[/color]'); } }, - + insertText: function(text, clearInputField) { if(clearInputField) { this.dom['inputField'].value = ''; } this.insert(text, ''); }, - + insertBBCode: function(bbCode) { - switch(bbCode) { + switch(bbCode) { case 'url': var url = prompt(this.lang['urlDialog'], 'http://'); if(url) @@ -2001,7 +2007,7 @@ var ajaxChat = { this.dom['inputField'].focus(); break; default: - this.insert('[' + bbCode + ']', '[/' + bbCode + ']'); + this.insert('[' + bbCode + ']', '[/' + bbCode + ']'); } }, @@ -2018,7 +2024,7 @@ var ajaxChat = { if (insText.length === 0) { range.move('character', -endTag.length); } else { - range.moveStart('character', startTag.length + insText.length + endTag.length); + range.moveStart('character', startTag.length + insText.length + endTag.length); } range.select(); } @@ -2052,7 +2058,7 @@ var ajaxChat = { + this.dom['inputField'].value.substr(pos); } }, - + replaceText: function(text) { try{ text = this.replaceLineBreaks(text); @@ -2063,20 +2069,20 @@ var ajaxChat = { text = this.replaceHyperLinks(text); text = this.replaceEmoticons(text); } - text = this.breakLongWords(text); + text = this.breakLongWords(text); text = this.replaceCustomText(text); } catch(e){ //alert(e); } return text; }, - + replaceCommands: function(text) { try { if(text.charAt(0) !== '/') { return text; } - var textParts = text.split(' '); + var textParts = text.split(' '); switch(textParts[0]) { case '/login': return this.replaceCommandLogin(textParts); @@ -2155,7 +2161,7 @@ var ajaxChat = { replaceCommandLogin: function(textParts) { return '' + this.lang['login'].replace(/%s/, textParts[1]) - + ''; + + ''; }, replaceCommandLogout: function(textParts) { @@ -2164,21 +2170,21 @@ var ajaxChat = { type = textParts[2]; return '' + this.lang['logout' + type].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandChannelEnter: function(textParts) { return '' + this.lang['channelEnter'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandChannelLeave: function(textParts) { return '' + this.lang['channelLeave'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandPrivMsg: function(textParts) { var privMsgText = textParts.slice(1).join(' '); privMsgText = this.replaceBBCode(privMsgText); @@ -2189,7 +2195,7 @@ var ajaxChat = { + ' ' + privMsgText; }, - + replaceCommandPrivMsgTo: function(textParts) { var privMsgText = textParts.slice(2).join(' '); privMsgText = this.replaceBBCode(privMsgText); @@ -2200,7 +2206,7 @@ var ajaxChat = { + ' ' + privMsgText; }, - + replaceCommandPrivAction: function(textParts) { var privActionText = textParts.slice(1).join(' '); privActionText = this.replaceBBCode(privActionText); @@ -2212,7 +2218,7 @@ var ajaxChat = { + this.lang['privmsg'] + ' '; }, - + replaceCommandPrivActionTo: function(textParts) { var privActionText = textParts.slice(2).join(' '); privActionText = this.replaceBBCode(privActionText); @@ -2222,9 +2228,9 @@ var ajaxChat = { + privActionText + ' ' + this.lang['privmsgto'].replace(/%s/, textParts[1]) - + ' '; + + ' '; }, - + replaceCommandAction: function(textParts) { var actionText = textParts.slice(1).join(' '); actionText = this.replaceBBCode(actionText); @@ -2232,9 +2238,9 @@ var ajaxChat = { actionText = this.replaceEmoticons(actionText); return '' + actionText - + ''; + + ''; }, - + replaceCommandInvite: function(textParts) { var inviteText = this.lang['invite'] .replace(/%s/, textParts[1]) @@ -2250,99 +2256,99 @@ var ajaxChat = { ); return '' + inviteText - + ''; + + ''; }, - + replaceCommandInviteTo: function(textParts) { var inviteText = this.lang['inviteto'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + inviteText - + ''; + + ''; }, - + replaceCommandUninvite: function(textParts) { var uninviteText = this.lang['uninvite'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + uninviteText - + ''; + + ''; }, - + replaceCommandUninviteTo: function(textParts) { var uninviteText = this.lang['uninviteto'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + uninviteText - + ''; + + ''; }, - + replaceCommandQueryOpen: function(textParts) { return '' + this.lang['queryOpen'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandQueryClose: function(textParts) { return '' + this.lang['queryClose'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreAdded: function(textParts) { return '' + this.lang['ignoreAdded'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreRemoved: function(textParts) { return '' + this.lang['ignoreRemoved'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreList: function(textParts) { return '' + this.lang['ignoreList'] + ' ' + this.getInlineUserMenu(textParts.slice(1)) - + ''; + + ''; }, - + replaceCommandIgnoreListEmpty: function(textParts) { return '' + this.lang['ignoreListEmpty'] - + ''; + + ''; }, - + replaceCommandKick: function(textParts) { return '' + this.lang['logoutKicked'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandWho: function(textParts) { return '' + this.lang['who'] + ' ' + this.getInlineUserMenu(textParts.slice(1)) - + ''; + + ''; }, replaceCommandWhoChannel: function(textParts) { return '' + this.lang['whoChannel'].replace(/%s/, textParts[1]) + ' ' + this.getInlineUserMenu(textParts.slice(2)) - + ''; + + ''; }, - + replaceCommandWhoEmpty: function(textParts) { return '' + this.lang['whoEmpty'] - + ''; + + ''; }, - + replaceCommandList: function(textParts) { var channels = textParts.slice(1); var listChannels = []; @@ -2362,9 +2368,9 @@ var ajaxChat = { return '' + this.lang['list'] + ' ' + listChannels.join(', ') - + ''; + + ''; }, - + replaceCommandBans: function(textParts) { var users = textParts.slice(1); var listUsers = []; @@ -2382,26 +2388,26 @@ var ajaxChat = { return '' + this.lang['bans'] + ' ' + listUsers.join(', ') - + ''; + + ''; }, - + replaceCommandBansEmpty: function(textParts) { return '' + this.lang['bansEmpty'] - + ''; + + ''; }, - + replaceCommandUnban: function(textParts) { return '' + this.lang['unban'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandWhois: function(textParts) { return '' + this.lang['whois'].replace(/%s/, textParts[1]) + ' ' + textParts[2] - + ''; + + ''; }, replaceCommandWhereis: function(textParts) { @@ -2416,24 +2422,24 @@ var ajaxChat = { + textParts[2] + '' ) - + ''; + + ''; }, - + replaceCommandRoll: function(textParts) { var rollText = this.lang['roll'].replace(/%s/, textParts[1]); rollText = rollText.replace(/%s/, textParts[2]); rollText = rollText.replace(/%s/, textParts[3]); return '' + rollText - + ''; + + ''; }, - + replaceCommandNick: function(textParts) { return '' + this.lang['nick'].replace(/%s/, textParts[1]).replace(/%s/, textParts[2]) - + ''; + + ''; }, - + replaceCommandError: function(textParts) { var errorMessage = this.lang['error'+textParts[1]]; if(!errorMessage) { @@ -2443,7 +2449,7 @@ var ajaxChat = { } return '' + errorMessage - + ''; + + ''; }, getInlineUserMenu: function(users) { @@ -2476,7 +2482,7 @@ var ajaxChat = { var openTags, closeTags, regExpOpenTags = /<[^>\/]+?>/gm, regExpCloseTags = /<\/[^>]+?>/gm; - + openTags = str.match(regExpOpenTags); closeTags = str.match(regExpCloseTags); // Return true if the number of tags doesn't match: @@ -2487,32 +2493,32 @@ var ajaxChat = { } return false; }, - + breakLongWords: function(text) { var newText, charCounter, currentChar, withinTag, withinEntity, i; - + if(!this.settings['wordWrap']) return text; - + newText = ''; charCounter = 0; - + for(i=0; i): if(i>5 && text.substr(i-5,4) === '
0 && text.charAt(i-1) === '>') { withinTag = false; // Reset the charCounter after newline tags (
): if(i>4 && text.substr(i-5,4) === '
0 && text.charAt(i-1) === ';') { @@ -2520,7 +2526,7 @@ var ajaxChat = { // We only increase the charCounter once for the whole entiy: charCounter++; } - + if(!withinTag && !withinEntity) { // Reset the charCounter if we encounter a word boundary: if(currentChar === ' ' || currentChar === '\n' || currentChar === '\t') { @@ -2534,14 +2540,14 @@ var ajaxChat = { newText += '​'; charCounter = 0; } - } + } // Add the current char to the text: newText += currentChar; } - + return newText; }, - + replaceBBCode: function(text) { if(!this.settings['bbCode']) { // If BBCode is disabled, just strip the text from BBCode tags: @@ -2549,11 +2555,11 @@ var ajaxChat = { } // Remove the BBCode tags: return text.replace( - /\[(\w+)(?:=([^<>]*?))?\](.+?)\[\/\1\]/gm, + /\[(\w+)(?:=([^<>]*?))?\](.+?)\[\/\1\]/gm, this.replaceBBCodeCallback ); }, - + replaceBBCodeCallback: function(str, p1, p2, p3) { // Only replace predefined BBCode tags: if(!ajaxChat.inArray(ajaxChat.bbCodeTags, p1)) { @@ -2585,7 +2591,7 @@ var ajaxChat = { if(this.settings['bbCodeColors']) { // Only allow predefined color codes: if(!attribute || !this.inArray(ajaxChat.colorCodes, attribute)) - return content; + return content; return '' + this.replaceBBCode(content) @@ -2593,7 +2599,7 @@ var ajaxChat = { } return content; }, - + replaceBBCodeUrl: function(content, attribute) { var url, regExpUrl; if(attribute) @@ -2657,13 +2663,13 @@ var ajaxChat = { + this.replaceBBCode(content.replace(/\t|(?: )/gm, '  ')) + ''; }, - + replaceBBCodeUnderline: function(content) { return '' + this.replaceBBCode(content) + ''; }, - + replaceHyperLinks: function(text) { var regExp; if(!this.settings['hyperLinks']) { @@ -2716,11 +2722,11 @@ var ajaxChat = { arguments.callee.regExp = new RegExp(regExpStr, 'gm'); } return text.replace( - arguments.callee.regExp, + arguments.callee.regExp, this.replaceEmoticonsCallback ); }, - + replaceEmoticonsCallback: function(str, p1, p2, p3) { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('(="[^"]*$)|(&[^;]*$)', ''); @@ -2728,9 +2734,9 @@ var ajaxChat = { // Avoid replacing emoticons in tag attributes or XHTML entities: if(p1.match(arguments.callee.regExp)) { return str; - } + } if(p2) { - var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes); + var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes); return ajaxChat.replaceEmoticons(p1) + '' ); } catch(e) { - //alert(e); + this.debugMessage('socketRegister', e); } } }, @@ -607,7 +609,7 @@ var ajaxChat = { } this.soundTransform.setVolume(volume); } catch(e) { - //alert(e); + this.debugMessage('setAudioVolume', e); } } }, @@ -626,7 +628,7 @@ var ajaxChat = { sound.load(urlRequest); } } catch(e) { - alert(e); + this.debugMessage('loadSounds', e); } }, @@ -660,7 +662,7 @@ var ajaxChat = { // sndTransform:SoundTransform (default = null) return this.sounds[soundID].play(0, 0, this.soundTransform); } catch(e) { - //alert(e); + this.debugMessage('playSound', e); } } return null; @@ -801,7 +803,7 @@ var ajaxChat = { try { clearTimeout(ajaxChat.timer); } catch(e) { - //alert(e); + this.debugMessage('makeRequest::clearTimeout', e); } try { if(data) { @@ -810,12 +812,12 @@ var ajaxChat = { ajaxChat.updateChatlistView(); } } catch(e) { - //alert(e); + this.debugMessage('makeRequest::logRetry', e); } try { ajaxChat.timer = setTimeout(function() { ajaxChat.updateChat(null); }, ajaxChat.timerRate); } catch(e) { - //alert(e); + this.debugMessage('makeRequest::setTimeout', e); } } }; @@ -2074,7 +2076,7 @@ var ajaxChat = { text = this.breakLongWords(text); text = this.replaceCustomText(text); } catch(e){ - //alert(e); + this.debugMessage('replaceText', e); } return text; }, @@ -2155,7 +2157,7 @@ var ajaxChat = { return this.replaceCustomCommands(text, textParts); } } catch(e) { - //alert(e); + this.debugMessage('replaceCommands', e); } return text; }, @@ -2885,7 +2887,7 @@ var ajaxChat = { this.socket.close(); this.socket = null; } catch(e) { - //alert(e); + this.debugMessage('finalize::closeSocket', e); } } this.persistSettings(); @@ -2960,6 +2962,16 @@ var ajaxChat = { // Return true if message is to be added to the chatList, else false customOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { return true; - } + }, + + debugMessage: function(msg, e) { + msg = 'Ajax chat: ' + msg + ' exception: '; + if (this.debug) { + console.log(msg, e); + if (this.debug == '2') { + alert(msg + e); + } + } + }, }; diff --git a/chat/js/config.js b/chat/js/config.js index 2f8c0d7..cd3be09 100644 --- a/chat/js/config.js +++ b/chat/js/config.js @@ -99,7 +99,7 @@ var ajaxChatConfig = { // Defines the sound that is played on error messages: soundError: 'sound_6', // Defines the sound that is played when private messages are received: - soundPrivate: 'sound_7', + soundPrivate: 'sound_7', // Defines if the document title blinks on new messages: blink: true, @@ -224,12 +224,14 @@ var ajaxChatConfig = { cookieDomain: null, // If enabled, cookies must be sent over secure (SSL/TLS encrypted) connections: cookieSecure: null, + // The name of the chat bot: chatBotName: 'ChatBot', // The userID of the chat bot: chatBotID: 2147483647, // Allow/Disallow registered users to delete their own messages: allowUserMessageDelete: true, + // Minutes until a user is declared inactive (last status update) - the minimum is 2 minutes: inactiveTimeout: 2, // UserID plus this value are private channels (this is also the max userID and max channelID): @@ -240,6 +242,7 @@ var ajaxChatConfig = { showChannelMessages: true, // Max messageText length: messageTextMaxLength: 1040, + // Defines if the socket server is enabled: socketServerEnabled: false, // Defines the hostname of the socket server used to connect from client side: @@ -247,5 +250,9 @@ var ajaxChatConfig = { // Defines the port of the socket server: socketServerPort: 1935, // This ID can be used to distinguish between different chat installations using the same socket server: - socketServerChatID: 0 -}; \ No newline at end of file + socketServerChatID: 0, + + // Debug allows console logging or alerts on caught errors - false/0 = no debug, true/1/2 = console log, 2 = alerts + debug: false + +} From b05f96607c10957f34421edfc356e211cf503b04 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 18:28:11 -0400 Subject: [PATCH 68/85] cleanup --- chat/changelog.txt | 1 + chat/js/config.js | 6 +----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/chat/changelog.txt b/chat/changelog.txt index 8f5ede6..3631af4 100644 --- a/chat/changelog.txt +++ b/chat/changelog.txt @@ -854,6 +854,7 @@ New Features: - Private messages now have their own sound - thanks to marquisite. - Added two new sounds - one is extra for your customization pleasure. - Added a custom user group and banned user group for customization and integration - thanks to marquisite. +- Added a debug mode to chat.js - thanks to Sophist-UK Bugfixes: - HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to \ No newline at end of file diff --git a/chat/js/config.js b/chat/js/config.js index cd3be09..31606f7 100644 --- a/chat/js/config.js +++ b/chat/js/config.js @@ -224,14 +224,12 @@ var ajaxChatConfig = { cookieDomain: null, // If enabled, cookies must be sent over secure (SSL/TLS encrypted) connections: cookieSecure: null, - // The name of the chat bot: chatBotName: 'ChatBot', // The userID of the chat bot: chatBotID: 2147483647, // Allow/Disallow registered users to delete their own messages: allowUserMessageDelete: true, - // Minutes until a user is declared inactive (last status update) - the minimum is 2 minutes: inactiveTimeout: 2, // UserID plus this value are private channels (this is also the max userID and max channelID): @@ -242,7 +240,6 @@ var ajaxChatConfig = { showChannelMessages: true, // Max messageText length: messageTextMaxLength: 1040, - // Defines if the socket server is enabled: socketServerEnabled: false, // Defines the hostname of the socket server used to connect from client side: @@ -254,5 +251,4 @@ var ajaxChatConfig = { // Debug allows console logging or alerts on caught errors - false/0 = no debug, true/1/2 = console log, 2 = alerts debug: false - -} +}; \ No newline at end of file From 1440573b94142aaa9997063be6bf7d1d5cb64263 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 18:43:28 -0400 Subject: [PATCH 69/85] more cleanup --- chat/js/chat.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index e0b1f47..c76595f 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -670,10 +670,9 @@ var ajaxChat = { playSoundOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { if(this.settings['audio'] && this.sounds && this.lastID && !this.channelSwitch) { - console.log(this.settings['soundPrivate']); + var messageParts = messageText.split(' ', 1); switch(userID) { case this.chatBotID: - var messageParts = messageText.split(' ', 1); switch(messageParts[0]) { case '/login': case '/channelEnter': @@ -701,7 +700,6 @@ var ajaxChat = { } break; default: - var messageParts = messageText.split(' ', 1); switch(messageParts[0]) { case '/privmsg': this.playSound(this.settings['soundPrivate']); From 58d77328d66b694069e43b90d7aa889d6bc1ee84 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 19:05:16 -0400 Subject: [PATCH 70/85] cleanup --- chat/changelog.txt | 3 ++- chat/js/chat.js | 55 ++++++++++++++++++++++++++++------------------ 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/chat/changelog.txt b/chat/changelog.txt index 3631af4..1e3e261 100644 --- a/chat/changelog.txt +++ b/chat/changelog.txt @@ -857,4 +857,5 @@ New Features: - Added a debug mode to chat.js - thanks to Sophist-UK Bugfixes: -- HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to \ No newline at end of file +- HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to gWorldz +- Nesting an [IMG] inside a [URL] tag will now only open the URL instead of also opening the image - thanks to James Almer. \ No newline at end of file diff --git a/chat/js/chat.js b/chat/js/chat.js index c76595f..8002e1f 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -85,11 +85,12 @@ var ajaxChat = { httpRequest: null, retryTimer: null, retryTimerDelay: null, - requestStatus: 'ok', + requestStatus: null, DOMbuffering: null, DOMbuffer: null, - DOMbufferRowClass: 'rowOdd', - debug: false, + DOMbufferRowClass: null, + inUrlBBCode: null, + debug: null, init: function(config, lang, initSettings, initStyle, initialize, initializeFunction, finalizeFunction) { this.httpRequest = {}; @@ -99,6 +100,9 @@ var ajaxChat = { this.lastID = 0; this.localID = 0; this.lang = lang; + this.requestStatus = 'ok'; + this.DOMbufferRowClass = 'rowOdd'; + this.inUrlBBCode = false; this.initConfig(config); this.initDirectories(); if(initSettings) { @@ -1587,7 +1591,7 @@ var ajaxChat = { }, formatDate: function(format, date) { - date = (date == null) ? new date() : date; + date = (date === null) ? new date() : date; return format .replace(/%Y/g, date.getFullYear()) @@ -2603,7 +2607,7 @@ var ajaxChat = { }, replaceBBCodeUrl: function(content, attribute) { - var url, regExpUrl; + var url, regExpUrl, link; if(attribute) url = attribute.replace(/\s/gm, this.encodeText(' ')); else @@ -2614,15 +2618,19 @@ var ajaxChat = { ); if(!url || !url.match(regExpUrl)) return content; - return '' + this.replaceBBCode(content) + ''; + this.inUrlBBCode = false; + return link; }, replaceBBCodeImage: function(url) { - var regExpUrl, maxWidth, maxHeight; + var regExpUrl, maxWidth, maxHeight, link; if(this.settings['bbCodeImages']) { regExpUrl = new RegExp( this.regExpMediaUrl, @@ -2633,16 +2641,21 @@ var ajaxChat = { url = this.stripTags(url.replace(/\s/gm, this.encodeText(' '))); maxWidth = this.dom['chatList'].offsetWidth-50; maxHeight = this.dom['chatList'].offsetHeight-50; - return '' - +''; + link = ''; + if(!this.inUrlBBCode) { + link = '' + + link + + ''; + } + return link; } return url; }, @@ -2791,7 +2804,7 @@ var ajaxChat = { }, setActiveStyleSheet: function(title) { - var i, a, main, titleFound = false; + var i, a,titleFound = false; for(i=0; (a = document.getElementsByTagName('link')[i]); i++) { if(a.getAttribute('rel').indexOf('style') !== -1 && a.getAttribute('title')) { a.disabled = true; @@ -2966,10 +2979,10 @@ var ajaxChat = { msg = 'Ajax chat: ' + msg + ' exception: '; if (this.debug) { console.log(msg, e); - if (this.debug == '2') { + if (this.debug === 2) { alert(msg + e); } } - }, + } -}; +}; \ No newline at end of file From da808498c2bc2676d1500f5c44da1aabeb4500c4 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 19:16:33 -0400 Subject: [PATCH 71/85] use new addEvent function to bind events (see d31c9d807cb58f403ac427b05ad6fc6dc4deab3f) --- chat/js/chat.js | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/chat/js/chat.js b/chat/js/chat.js index 8002e1f..9a4b260 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -316,19 +316,12 @@ var ajaxChat = { }, setLoadHandler: function() { - // Make sure initialize() is called on page load: - var onload = window.onload; - if(typeof onload !== 'function') { - window.onload = function() { - ajaxChat.initialize(); - }; - } else { - window.onload = function() { - onload(); - ajaxChat.initialize(); - }; - } - }, + var self = this; + // Make sure initialize() is called on page load: + this.addEvent(window,'load', function() { + self.initialize(); + }); + }, setUnloadHandler: function() { // Make sure finalize() is called on page unload: @@ -1858,6 +1851,18 @@ var ajaxChat = { } }, + addEvent: function(elem, type, eventHandle) { + if (!elem) + return; + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } else { + elem["on"+type]=eventHandle; + } + }, + getClass: function(node) { if(typeof node.className !== 'undefined') { return node.className; // IE From 60bbdf2543aa3b93d1321ace76a570be142f64b3 Mon Sep 17 00:00:00 2001 From: Frug Date: Tue, 1 Jul 2014 19:24:11 -0400 Subject: [PATCH 72/85] accept 0 for ban time - thanks @micheal-swiggs issue #39 --- chat/changelog.txt | 3 ++- chat/lib/class/AJAXChat.php | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/chat/changelog.txt b/chat/changelog.txt index 1e3e261..ff0f55b 100644 --- a/chat/changelog.txt +++ b/chat/changelog.txt @@ -858,4 +858,5 @@ New Features: Bugfixes: - HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to gWorldz -- Nesting an [IMG] inside a [URL] tag will now only open the URL instead of also opening the image - thanks to James Almer. \ No newline at end of file +- Nesting an [IMG] inside a [URL] tag will now only open the URL instead of also opening the image - thanks to James Almer. +- Users can now be kicked for 0 minutes, which is effectively a kick and not a ban - thanks to micheal-swiggs \ No newline at end of file diff --git a/chat/lib/class/AJAXChat.php b/chat/lib/class/AJAXChat.php index 2cf7aa1..ac9c71b 100644 --- a/chat/lib/class/AJAXChat.php +++ b/chat/lib/class/AJAXChat.php @@ -1627,7 +1627,7 @@ function kickUser($userName, $banMinutes=null, $userID=null) { return; } - $banMinutes = $banMinutes ? $banMinutes : $this->getConfig('defaultBanTime'); + $banMinutes = ($banMinutes !== null) ? $banMinutes : $this->getConfig('defaultBanTime'); if($banMinutes) { // Ban User for the given time in minutes: From 55d947ba66a4e802d5e312528a2a0c68e2d1be57 Mon Sep 17 00:00:00 2001 From: marquisite Date: Thu, 3 Jul 2014 17:24:55 +0930 Subject: [PATCH 73/85] Case-insensitive online user list Modification originally posted to Google Groups by Ingrid --- chat/lib/class/AJAXChat.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chat/lib/class/AJAXChat.php b/chat/lib/class/AJAXChat.php index 2cf7aa1..be8a90a 100644 --- a/chat/lib/class/AJAXChat.php +++ b/chat/lib/class/AJAXChat.php @@ -2389,7 +2389,7 @@ function getOnlineUsersData($channelIDs=null, $key=null, $value=null) { FROM '.$this->getDataBaseTable('online').' ORDER BY - userName;'; + LOWER(userName);'; // Create a new SQL query: $result = $this->db->sqlQuery($sql); @@ -3323,4 +3323,4 @@ function &getAllChannels() { } } -?> \ No newline at end of file +?> From f1f974bcae3925d182f9fe2768a2115cd08e4911 Mon Sep 17 00:00:00 2001 From: Joshua Embrey Date: Sat, 19 Jul 2014 01:23:20 -0400 Subject: [PATCH 74/85] Update AJAXChat.php Removed mt_srand and seed; seeding is performed automatically in PHP5+. --- chat/lib/class/AJAXChat.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/chat/lib/class/AJAXChat.php b/chat/lib/class/AJAXChat.php index ac9c71b..5448890 100644 --- a/chat/lib/class/AJAXChat.php +++ b/chat/lib/class/AJAXChat.php @@ -1613,9 +1613,6 @@ function getSocketRegistrationID() { } function rollDice($sides) { - // seed with microseconds since last "whole" second: - mt_srand((double)microtime()*1000000); - return mt_rand(1, $sides); } @@ -3323,4 +3320,4 @@ function &getAllChannels() { } } -?> \ No newline at end of file +?> From e106130450fbd47c061c8398d21e8e2e6aeb0c19 Mon Sep 17 00:00:00 2001 From: Frug Date: Sat, 19 Jul 2014 16:46:41 -0400 Subject: [PATCH 75/85] Merge a bunch of changes from testing. Responsive layout, themes, PM color and sounds --- chat/changelog.txt | 18 +- chat/css/Cobalt.css | 208 +- chat/css/Core.css | 197 +- chat/css/Lithium.css | 200 +- chat/css/Mercury.css | 273 +- chat/css/MyBB.css | 197 +- chat/css/Oxygen.css | 202 +- chat/css/Pine.css | 142 + chat/css/Plum.css | 221 +- chat/css/Sulfur.css | 200 +- chat/css/Uranium.css | 360 +- chat/css/XenForo.css | 238 ++ chat/css/beige.css | 202 +- chat/css/black.css | 208 +- chat/css/custom.css | 35 +- chat/css/fonts.css | 19 +- chat/css/global.css | 296 +- chat/css/grey.css | 203 +- chat/css/ie5-6.css | 70 - chat/css/pine_images/chatlist.jpg | Bin 0 -> 50564 bytes chat/css/print.css | 23 +- chat/css/prosilver.css | 276 +- chat/css/shoutbox.css | 4 + chat/css/subSilver.css | 118 - chat/css/subblack2.css | 118 - chat/css/vBulletin.css | 225 +- chat/img/audio.png | Bin 3966 -> 0 bytes chat/img/autoscroll.png | Bin 4181 -> 0 bytes chat/img/buttons-sprite.png | Bin 0 -> 8327 bytes chat/img/help.png | Bin 1359 -> 0 bytes chat/img/playback.png | Bin 961 -> 0 bytes chat/img/settings.png | Bin 1151 -> 0 bytes chat/img/users.png | Bin 1277 -> 0 bytes chat/index.php | 6 +- chat/js/FABridge.js | 1182 ++--- chat/js/chat.js | 731 ++-- chat/js/config.js | 74 +- chat/js/lang/cy.js | 184 +- chat/js/lang/el.js | 182 +- chat/js/lang/ka.js | 2 +- chat/js/lang/kr.js | 2 +- chat/js/lang/nl-be.js | 180 +- chat/js/lang/no.js | 182 +- chat/js/lang/pl.js | 182 +- chat/js/lang/sl.js | 2 +- chat/lib/class/AJAXChat.php | 6650 ++++++++++++++--------------- chat/lib/config.php.example | 2 + chat/lib/lang/ar.php | 250 +- chat/lib/lang/ca.php | 252 +- chat/lib/lang/cy.php | 250 +- chat/lib/lang/es.php | 250 +- chat/lib/lang/fr.php | 252 +- chat/lib/lang/ka.php | 2 +- chat/lib/lang/kr.php | 2 +- chat/lib/lang/nl-be.php | 250 +- chat/lib/lang/nl.php | 250 +- chat/lib/lang/no.php | 250 +- chat/lib/lang/pl.php | 250 +- chat/lib/lang/sl.php | 250 +- chat/lib/template/loggedIn.html | 642 +-- chat/lib/template/loggedOut.html | 51 +- chat/lib/template/logs.html | 42 +- chat/readme.html | 936 ++-- chat/sounds/sound_7.mp3 | Bin 0 -> 24241 bytes chat/sounds/sound_8.mp3 | Bin 0 -> 33436 bytes 65 files changed, 9103 insertions(+), 8890 deletions(-) create mode 100644 chat/css/Pine.css create mode 100644 chat/css/XenForo.css delete mode 100644 chat/css/ie5-6.css create mode 100644 chat/css/pine_images/chatlist.jpg delete mode 100644 chat/css/subSilver.css delete mode 100644 chat/css/subblack2.css delete mode 100644 chat/img/audio.png delete mode 100644 chat/img/autoscroll.png create mode 100644 chat/img/buttons-sprite.png delete mode 100644 chat/img/help.png delete mode 100644 chat/img/playback.png delete mode 100644 chat/img/settings.png delete mode 100644 chat/img/users.png create mode 100644 chat/sounds/sound_7.mp3 create mode 100644 chat/sounds/sound_8.mp3 diff --git a/chat/changelog.txt b/chat/changelog.txt index 733eb99..fd30e78 100644 --- a/chat/changelog.txt +++ b/chat/changelog.txt @@ -843,4 +843,20 @@ Bugfixes: - Fix potential conflict between php and mysql timezones by using mysql's FROM_UNIXTIME(). - thanks to ManOnDaMoon. - Normalized sound volumes a bit. - Fixed mybb integration database connection. Connection details should be pulled automatically now. -- Fidex mybb integration guest logins to accept guests that don't enter a username (assign numbers like other versions). \ No newline at end of file +- Fidex mybb integration guest logins to accept guests that don't enter a username (assign numbers like other versions). + +Version 0.8.8 (?.?.2014) +------------------------------- +Notice: +- If you are using the javascript override handleCustomInfoMessage in custom.js, you should make it return true if you capture an event + +New Features: +- Private messages now have their own sound - thanks to marquisite. +- Added two new sounds - one is extra for your customization pleasure. +- Added a custom user group and banned user group for customization and integration - thanks to marquisite. +- Added a debug mode to chat.js - thanks to Sophist-UK. + +Bugfixes: +- HTML is now stripped from [IMG] tag URLs, and tag parsing errors will no longer crash chat. - thanks to gWorldz. +- Nesting an [IMG] inside a [URL] tag will now only open the URL instead of also opening the image - thanks to James Almer. +- Users can now be kicked for 0 minutes, which is effectively a kick and not a ban - thanks to micheal-swiggs. \ No newline at end of file diff --git a/chat/css/Cobalt.css b/chat/css/Cobalt.css index 5a87c01..d4e89e6 100644 --- a/chat/css/Cobalt.css +++ b/chat/css/Cobalt.css @@ -2,122 +2,114 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by PunBB style "Cobalt": * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#1a1a1a; + color:#ababab; + border: 0; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-color:#1D68E0; + color:#000; +} +#content select, #loginForm select, #loginForm input, #content textarea { + background-color:#383838; + color:#ababab; + border: 1px solid #565656; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#1a1a1a; - color:#ababab; - border: 0; - } - #content select, #loginForm select, #loginForm input, #content textarea { - background-color:#383838; - color:#ababab; - border: 1px solid #565656; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#2A2A2A; - color:#D4D4D4; - } - #loginContent h1 { - color:#D4D4D4; - } - #loginContent a { - color:#60A0DC; - } - #loginContent #loginFormContainer #loginButton { - background-color:#424242; - color:#D4D4D4; - } - #loginContent #errorContainer { - color:red; - } - - #content { - background-color:#2A2A2A; - color:#D4D4D4; - } - #content h1 { - color:#D4D4D4; - } - #content a { - color:#60A0DC; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer { - border: 1px solid #565656; - background-color:#383838; - } - #content #bbCodeContainer, #content #emoticonsContainer { - background-color:#383838; - padding: 5px; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#565656; - } - #content .rowOdd { - background-color:#484848; - } - #content .guest { - color:gray; - } - #content .user { - color:#D4D4D4; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#60A0DC; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#60A0DC; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#383838; - color:#D4D4D4; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#2A2A2A; + color:#D4D4D4; +} +.ajax-chat h1 { + color:#D4D4D4; +} +.ajax-chat a { + color:#60A0DC; +} +#loginContent #loginButton { + background-color:#424242; + color:#D4D4D4; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer { + border: 1px solid #565656; + background-color:#383838; +} +.ajax-chat .popup { + background-color:#383838; + padding:5px; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#565656; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#484848; +} +#content #chatList .rowOdd.private { + background-color:#B83C1D; +} +#content #chatList .rowEven.private { + background-color:#C84A24; +} +#content .guest { + color:gray; +} +#content .user { + color:#D4D4D4; +} +#content .customUser { + color:#acc2d7; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#60A0DC; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#60A0DC; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#383838; + color:#D4D4D4; } \ No newline at end of file diff --git a/chat/css/Core.css b/chat/css/Core.css index d6f751b..06de2ac 100644 --- a/chat/css/Core.css +++ b/chat/css/Core.css @@ -2,115 +2,116 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by Simple Machines Forum style "SMF Default Theme - Core": * http://www.simplemachines.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#CDE7FF; + color:#333333; + border: 1px solid #787878; +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #787878; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#CDE7FF; - color:#333333; - border: 1px solid #787878; - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #787878; - } +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - #loginContent { - background-color:#E5E5E8; - color:#000; - } - #loginContent h1 { - color:#000; - } - #loginContent a { - color:#000; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#E5E5E8; - color:#000; - } - #content h1 { - color:#000; - } - #content a { - color:#000; - } - #content input, #content select, #content textarea { - background-color:#FFF; - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #emoticonsContainer { - border-color:#ADADAD; +/* Other Theme Elements */ +.ajax-chat { + background-color:#E5E5E8; + color:#000; +} +.ajax-chat h1 { + color:#000; +} +.ajax-chat a { + color:#000; +} +#loginContent #errorContainer { + color:red; +} +#content input, #content select, #content textarea { + background-color:#FFF; + color:#000; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + border-color:#ADADAD; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#ADADAD; + border:1px solid gray; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#ECEDF3; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#F6F6F6; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#003d8e; +} +#content .moderator { + color:#0000FF; +} +#content .admin { + color:#FF0000; +} +#content .chatBot { + color:#476C8E; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#476C8E; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#88A6C0; + color:#FFF; +} + +@media (max-width: 700px) { + #content #emoticonsContainer { background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#ECEDF3; - } - #content .rowOdd { - background-color:#F6F6F6; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#0000FF; - } - #content .admin { - color:#FF0000; - } - #content .chatBot { - color:#476C8E; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#476C8E; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#88A6C0; - color:#FFF; + border: 1px solid #ADADAD; } } \ No newline at end of file diff --git a/chat/css/Lithium.css b/chat/css/Lithium.css index a9d5ed8..07cf534 100644 --- a/chat/css/Lithium.css +++ b/chat/css/Lithium.css @@ -2,116 +2,112 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by PunBB style "Lithium": * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#6C8A3F; + color: #fff; + border:1px solid #6C8A3F; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-color:#5E7D2E; +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #6C8A3F; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#6C8A3F; - color: #fff; - border: 1px solid #6C8A3F; - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #6C8A3F; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#F1F1F1; - color:#333333; - } - #loginContent h1 { - color:#333333; - } - #loginContent a { - color:#638137; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#333333; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#F1F1F1; - color:#333333; - } - #content h1 { - color:#333333; - } - #content a { - color:#638137; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:#6C8A3F; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#F1F1F1; - } - #content .rowOdd { - background-color:#DEDFDF; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#638137; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#638137; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#6C8A3F; - color:#FFF; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#F1F1F1; + color:#333; +} +.ajax-chat h1 { + color:#333333; +} +.ajax-chat a { + color:#638137; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#333333; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:#6C8A3F; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#FFF; + border:1px solid #6C8A3F; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#F1F1F1; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#DEDFDF; +} +#content #chatList .rowOdd.private { + background-color:#84CF86; +} +#content #chatList .rowEven.private { + background-color:#9FD598; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#406021; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#638137; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#638137; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#6C8A3F; + color:#FFF; } \ No newline at end of file diff --git a/chat/css/Mercury.css b/chat/css/Mercury.css index 38dff16..1fe0530 100644 --- a/chat/css/Mercury.css +++ b/chat/css/Mercury.css @@ -2,154 +2,147 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by PunBB style "Mercury": * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content input, #content select, #content textarea { + border: 0; +} +#content textarea { + border-color: #383838; +} +#content input { + border-radius: 5px; +} - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - #content input, #content select, #content textarea { - border: 1px solid #565656; - } - #content textarea { - border-color: #383838; - } - #content input { - border-radius: 3px; - } - #loginContent { - background-color:#2A2A2A; - color:#D4D4D4; - } - #loginContent h1 { - color:#D4D4D4; - } - #loginContent a { - color:#F6B620; - } - #loginContent input, #loginContent select { - background-color:#424242; - border-color:#565656; - color:#D4D4D4; - } - #loginContent #loginFormContainer #loginButton { - background-color:#424242; - color:#D4D4D4; - border-radius: 3px; - } - #loginContent #errorContainer { - color:red; - } - - #content { - background-color:#2A2A2A; - color:#D4D4D4; - } - #content h1 { - color:#D4D4D4; - } - #content a { - color:#F6B620; - } - #content input, #content select, #content textarea { - background-color:#383838; - color:#D4D4D4; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #emoticonsContainer { - border: 0; - background-color:#383838; - } - #content #colorCodesContainer { - padding: 5px; - box-shadow: 2px 2px 4px #000; - border-radius: 3px; - } - #content #onlineListContainer { - background: #383838; - } - #content #onlineListContainer #onlineList div { - margin: 0 1px 1px 1px; - border-radius: 5px; - background: #404040; - } - #content #onlineListContainer #onlineList ul { - margin-top: 1px; - list-style: none; - } - #content #emoticonsContainer { - border-radius: 3px; - } - #content #bbCodeContainer { - border: 0; padding-left: 0; - } - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton { - background-color:#383838; - color:#D4D4D4; - } - #content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover { - background-color:#565656; - } - #content #optionsContainer input.button { - border: 0; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#505050; - } - #content .rowOdd { - background-color:#484848; - } - #content .guest { - color:gray; - } - #content .user { - color:#D4D4D4; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#F6B620; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#F6B620; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - color:#D4D4D4; - height: 15px; - } -} \ No newline at end of file +/* Other Theme Elements */ +.ajax-chat { + background-color:#2A2A2A; + color:#D4D4D4; +} +.ajax-chat h1 { + color:#D4D4D4; +} +.ajax-chat a { + color:#F6B620; +} +#loginContent input, #loginContent select { + background-color:#424242; + border-color:#565656; + color:#D4D4D4; +} +#loginContent #loginButton { + background-color:#424242; + color:#D4D4D4; + border-radius: 3px; +} +#loginContent #errorContainer { + color:red; +} +#content input, #content select, #content textarea { + background-color:#383838; + color:#D4D4D4; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + border:0; + background-color:#383838; +} +.ajax-chat .popup { + padding:5px; + box-shadow:2px 2px 4px #000; + border-radius:3px; + background-color:#424242; + border:1px solid #333; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #onlineListContainer { + background: #383838; +} +#content #onlineListContainer #onlineList div { + margin: 0 1px 1px 1px; + border-radius: 5px; + background: #404040; +} +#content #onlineListContainer #onlineList ul { + margin-top: 1px; + list-style: none; +} +#content #bbCodeContainer { + border: 0; padding-left: 0; +} +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton { + background-color:#383838; + color:#D4D4D4; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover { + background-color:#565656; +} +#content #optionsContainer input.button { + border: 0; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#505050; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#484848; +} +#content #chatList .rowOdd.private { + background-color:#101010; +} +#content #chatList .rowEven.private { + background-color:#1f1f1f; +} +#content .guest { + color:gray; +} +#content .user { + color:#D4D4D4; +} +#content .customUser { + color:#e0ca95; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#F6B620; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#F6B620; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + color:#D4D4D4; + height: 15px; +} diff --git a/chat/css/MyBB.css b/chat/css/MyBB.css index 8220875..e71f624 100644 --- a/chat/css/MyBB.css +++ b/chat/css/MyBB.css @@ -2,115 +2,108 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by MyBB style "MyBB Default": - * http://www.mybboard.net/ + * http://www.mybb.com/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { - - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#02619f; - color:#fff; - font-weight: bold; - border: 0px solid #02619f; - border-radius: 5px; - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #02619f; - border-radius: 5px; - } +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#02619f; + color:#fff; + font-weight: bold; + border: 0px solid #02619f; + border-radius: 5px; +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #02619f; + border-radius: 5px; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - #loginContent { - background-color:#FFF; - color:#000; - } - #loginContent h1 { - color:#000; - } - #loginContent a { - color:#000; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#FFF; - color:#000; - } - #content h1 { - color:#000; - } - #content a { - color:#000; - } - #content input, #content select, #content textarea { - background-color:#FFF; - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #emoticonsContainer { - border-color:#ADADAD; - background-color:#E5E5E8; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven, #content .rowOdd { - background-color:#EFEFEF; - border-bottom: 1px solid #bdccf7; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#0000FF; - } - #content .admin { - color:#FF0000; - } - #content .chatBot { - color:#476C8E; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#476C8E; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#02619f; - color:#FFF; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#FFF; + color:#000; +} +.ajax-chat h1 { + color:#000; +} +.ajax-chat a { + color:#000; +} +#loginContent #errorContainer { + color:red; +} +#content input, #content select, #content textarea { + background-color:#FFF; + color:#000; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer { + border-color:#ADADAD; + background-color:#E5E5E8; +} +.ajax-chat .popup { + border:1px solid #ADADAD; + background-color:#E5E5E8; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #content .rowOdd { + background-color:#EFEFEF; + border-bottom: 1px solid #bdccf7; +} +#content #chatList .private { + background-color:#F8D0D0; + border-bottom:1px solid #ED3950; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#02619f; +} +#content .moderator { + color:#0000FF; +} +#content .admin { + color:#FF0000; +} +#content .chatBot { + color:#476C8E; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#476C8E; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#02619f; + color:#FFF; } \ No newline at end of file diff --git a/chat/css/Oxygen.css b/chat/css/Oxygen.css index b64a869..0009723 100644 --- a/chat/css/Oxygen.css +++ b/chat/css/Oxygen.css @@ -2,116 +2,114 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by PunBB style "Oxygen": * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#0066B9; + background-image: linear-gradient(to bottom, #4795CC 0px, #2A6AB8 100%); + color: #fff; + border: 1px solid #0066B9; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-image:linear-gradient(to top, #4795CC 0px, #2A6AB8 100%); +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #0066B9; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#0066B9; - color: #fff; - border: 1px solid #0066B9; - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #0066B9; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#F1F1F1; - color:#333333; - } - #loginContent h1 { - color:#333333; - } - #loginContent a { - color:#0066B9; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#333333; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#F1F1F1; - color:#333333; - } - #content h1 { - color:#333333; - } - #content a { - color:#0066B9; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:#0066B9; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#F1F1F1; - } - #content .rowOdd { - background-color:#DEDFDF; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#0066B9; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#0066B9; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#0066B9; - color:#FFF; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#F1F1F1; + color:#333; +} +.ajax-chat h1 { + color:#333333; +} +.ajax-chat a { + color:#0066B9; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#333333; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:#0066B9; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#FFF; + border:1px solid #0066B9; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#F1F1F1; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#E6E7E7; +} +#content #chatList .rowOdd.private { + background-color:#BCC5FF; +} +#content #chatList .rowEven.private { + background-color:#C7D1FC; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#003366; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#0066B9; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#0066B9; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#0066B9; + background-image: linear-gradient(to bottom, #2A6AB8 0px, #4795CC 100%); + color:#FFF; } \ No newline at end of file diff --git a/chat/css/Pine.css b/chat/css/Pine.css new file mode 100644 index 0000000..8d831f3 --- /dev/null +++ b/chat/css/Pine.css @@ -0,0 +1,142 @@ +/* + * @package AJAX_Chat + * @author Rosina Ramirez + */ +@import url('global.css'); +@import url('fonts.css'); +@import url('print.css'); +@import url('custom.css'); + +#content input, select, #content textarea, #loginButton { + border: 0; +} +#loginButton{ + box-shadow:0px 1px 4px 0px rgba(0,0,0,0.5); +} +#content textarea { + border-color: #383838; +} +.ajax-chat { + background-color:#0f2314; + color:#f0f0f0; +} +.ajax-chat a, .ajax-chat a:visited, .ajax-chat h1{ + color:#C5EBB7; +} +input, select { + background:#0A140C; + border: 1px solid rgba(0,0,0,1); + color:#C5EBB7; +} +#loginContent #errorContainer { + color:#FF6262; +} + +#content textarea { + background:rgba(0,0,0,0.5); + color:#C5EBB7; +} +#content #chatList{ + background: url('pine_images/chatlist.jpg') no-repeat bottom right #0a180d; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer{ + box-shadow: inset 1px 1px 10px rgba(0,0,0,0.6); + border:0 +} +#content #colorCodesContainer a:first-child{ + border-left: 1px solid #000; +} +#content #colorCodesContainer a { + border-color: #000; + border-left: 0; +} +#content #colorCodesContainer { + background: #000; + background: rgba(0,0,0,0.4); + border: 0; +} +#content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + background: rgba(0,0,0,0.3) +} +#content #onlineListContainer #onlineList ul { + list-style: none; +} +#content #emoticonsContainer { + border-radius: 3px; +} +#content #bbCodeContainer { + border: 0; + padding-left: 0; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} + +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginButton { + background-color:#4C9C1F; + color:#fff; + padding:6px 12px +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginButton:hover { + background-color:#57B324; +} +#content #optionsContainer input.button { + border: 0; +} +#content #optionsContainer input { + background-color:transparent; +} +.ajax-chat .popup { + background-color:#F7F5DC; + border: 1px solid #808080; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background:#2C4E3D; + background:rgba(44, 78, 61, 0.6) +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background:#416453; + background:rgba(65, 100, 83, 0.6) +} +#content #chatList .rowOdd.private { + background-color:#00701C; +} +#content #chatList .rowEven.private { + background-color:#007B07; +} +#content .guest { + color:#B8B8B8; +} +#content .user { + color:#C8E4CF; +} +#content .customUser { + color:#4c9c1f; +} +#content .moderator { + color:#71E271; +} +#content .admin { + color:#FF6262; +} +#content .chatBot { + color:#E7B98E; +} +#content #chatList .chatBotErrorMessage { + color:#FF6262; +} +#content #chatList .deleteSelected { + border-color:#FF6262; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + color:#C5EBB7; + height: 15px; +} \ No newline at end of file diff --git a/chat/css/Plum.css b/chat/css/Plum.css index 2b8c304..f79d446 100644 --- a/chat/css/Plum.css +++ b/chat/css/Plum.css @@ -1,113 +1,134 @@ /* * @package AJAX_Chat * @author Rosina Ramirez - * @copyright (c) Rosina Ramirez - * @license Modified MIT License - * @link https://blueimp.net/ajax/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +.ajax-chat { + background-color:#2A112F; + color:#9765A0; +} +.ajax-chat a { + color:#DA83A1; +} +.ajax-chat h1 { + color:#CDA6D2; +} +#content input, select, #content textarea, #loginButton { + border: 0; box-shadow:1px 1px 4px 0px rgba(0,0,0,0.5); +} +#content textarea { + border-color:#383838; +} +#content input, #loginButton { + border-radius:8px; +} +#content input, #loginContent input, #content select, #loginContent select { + background:#17081a; + border:1px solid rgba(0,0,0,1); + color:#9765A0; +} +#loginContent #errorContainer { + color:red; +} +#content textarea { + background:rgba(0,0,0,0.3); + color:#9765A0; +} +#content #chatList{ + background: url('plum_images/plum.png') no-repeat bottom right rgba(0,0,0,0.3); +} +.ajax-chat #content .popup { + padding: 5px; + box-shadow: 2px 2px 4px #000; + border-radius: 3px; + background:rgba(225,225,225,0.2); + border-color:#9765A0; +} +#content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + background: url('plum_images/plum2.png') no-repeat bottom left rgba(0,0,0,0.3); +} +#content #onlineListContainer #onlineList ul { + list-style: none; +} +#content #emoticonsContainer { + border-radius:3px; +} +#content #bbCodeContainer { + border:0; + padding-left:0; +} - #content input, select, #content textarea, #loginButton { - border: 0; box-shadow:1px 1px 4px 0px rgba(0,0,0,0.5); - } - #content textarea { border-color: #383838; } - #content input, #loginButton { border-radius: 8px; } - #loginContent, #content { - background-color:#2A112F; - color:#9765A0; - } - a { - color:#DA83A1; - } - input, select { - background:#17081a; - border: 1px solid rgba(0,0,0,1); - color:#9765A0; - } - #loginContent #errorContainer { - color:red; - } - h1 { - color:#CDA6D2; - } - - #content textarea { - background:rgba(0,0,0,0.3); - color:#9765A0; - } - #content #chatList{ - background: url('plum_images/plum.png') no-repeat bottom right rgba(0,0,0,0.3); - } - #content #colorCodesContainer { - padding: 5px; - box-shadow: 2px 2px 4px #000; - border-radius: 3px; - background:rgba(225,225,225,0.2) - } +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} + +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginButton { + background-color:#461124; + color:#DAABBC; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginButton:hover { + background-color:#591E33; +} +#content #optionsContainer input.button { + border: 0; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background:rgba(0,0,0,0.2); +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background:rgba(0,0,0,0.3); +} +#content #chatList .rowOdd.private { + background-color:#700030; +} +#content #chatList .rowEven.private { + background-color:#7B0039; +} +#content .guest { + color:#CD9595; +} +#content .user { + color:#CDA6D2; +} +#content .customUser { + color:#da83a1; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#CDA6D2; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + color:#CDA6D2; + height: 15px; +} + +@media (max-width: 480px) { #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { - background: url('plum_images/plum2.png') no-repeat bottom left rgba(0,0,0,0.3); - } - #content #onlineListContainer #onlineList ul { - list-style: none; - } - #content #emoticonsContainer { border-radius: 3px; } - #content #bbCodeContainer { border: 0; padding-left: 0; } - #content #statusIconContainer { background-image: url('../img/loading-sprite.png'); } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginButton { - background-color:#461124; - color:#DAABBC; - } - #content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginButton:hover { - background-color:#591E33; - } - #content #optionsContainer input.button { border: 0; } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background:rgba(0,0,0,0.2); - } - #content .rowOdd { - background:rgba(0,0,0,0.3); - } - #content .guest { - color:gray; - } - #content .user { - color:#CDA6D2; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#CDA6D2; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - color:#CDA6D2; - height: 15px; + background:#2A112F; } } \ No newline at end of file diff --git a/chat/css/Sulfur.css b/chat/css/Sulfur.css index 384141a..d0ed944 100644 --- a/chat/css/Sulfur.css +++ b/chat/css/Sulfur.css @@ -2,116 +2,112 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by PunBB style "Sulfur": * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#B84623; + color: #fff; + border: 1px solid #B84623; +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-color:#9A2806; +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #B84623; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#B84623; - color: #fff; - border: 1px solid #B84623; - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #B84623; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#F1F1F1; - color:#333333; - } - #loginContent h1 { - color:#333333; - } - #loginContent a { - color:#B84623; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#333333; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#F1F1F1; - color:#333333; - } - #content h1 { - color:#333333; - } - #content a { - color:#B84623; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:#B84623; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#F1F1F1; - } - #content .rowOdd { - background-color:#DEDFDF; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#B84623; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#B84623; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#B84623; - color:#FFF; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#F1F1F1; + color:#333333; +} +.ajax-chat h1 { + color:#333; +} +.ajax-chat a { + color:#B84623; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#333; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:#B84623; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#FFF; + border:1px solid #B84623; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#F1F1F1; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#DEDFDF; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#602010; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#B84623; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#B84623; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#B84623; + color:#FFF; } \ No newline at end of file diff --git a/chat/css/Uranium.css b/chat/css/Uranium.css index 062c970..b8ac92f 100644 --- a/chat/css/Uranium.css +++ b/chat/css/Uranium.css @@ -1,179 +1,219 @@ /* * @package AJAX_Chat * @author Rosina Ramirez - * @copyright (c) Rosina Ramirez - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - * */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); +html { + height: 100%; +} +input, select, textarea, #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3, #content #inputFieldContainer, #loginContent input { + border-radius:5px; + -webkit-border-radius:5px; + border:0; +} +#content #inputFieldContainer{ + background:#0E0E0E; + padding:0; +} +#content #inputFieldContainer #inputField{ + box-shadow: inset 1px 1px 5px 1px #000; + border:0; + padding:3px 0.5%; + width: 99%; +} +#content #inputFieldContainer #inputField:focus{ + outline:none; + box-shadow:0px 0px 8px 1px #376e34; +} +#loginContent #loginButton { + background-color:#424242; + color:#b0b8a8; + border-radius: 3px; +} +#loginContent #errorContainer { + color:red; +} -@media screen,projection,handheld { - - input, select, textarea, #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content .rowEven, - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3, #content #inputFieldContainer, #loginContent input - { border-radius: 5px; -webkit-border-radius:5px;border:0;} - #loginContent a { - color:#4c9546; - } - #content #inputFieldContainer{height:46px; background:#0E0E0E;padding:0;} - #content #inputFieldContainer #inputField{box-shadow: inset 1px 1px 5px 1px #000; border:0;height: 40px;padding: 3px 0.5%;width: 99%;} - #content #inputFieldContainer #inputField:focus{outline:none;box-shadow:0px 0px 8px 1px #376e34;} - #loginContent #loginFormContainer #loginButton { - background-color:#424242; - color:#b0b8a8; - border-radius: 3px; - } - #loginContent #errorContainer { - color:red; - } - - #content, #loginContent { +.ajax-chat { + height: 100%; background-color: #464646; - background-image: -webkit-gradient(linear, left top, left bottom, from(#464646), to(#181818)); background-image: -webkit-linear-gradient(top, #464646, #181818); background-image: -moz-linear-gradient(top, #464646, #181818); background-image: -o-linear-gradient(top, #464646, #181818); background-image: linear-gradient(to bottom, #464646, #181818); color:#b0b8a8; - } - h1, h3 { - color:#e2e9db; - text-shadow: 1px 1px 2px #191919; - } - #content a { - color:#4c9546; - } - #loginContent input, #loginContent select{background:#171717; color:#b0b8a8;} - input, select{padding: 3px;} - #content input,#content select,#content textarea { - background-color:#0e0e0e; - color:#b0b8a8; - } - #content #colorCodesContainer,#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #inputFieldContainer, - #loginContent input[type=text], #loginContent input[type=password]{ - box-shadow: 0px 0px 8px 1px #495a49; -webkit-box-shadow: 0px 0px 8px 1px #495a49; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { - background-color:#171717; - border:0; - } - #content #chatList:after, #content #chatList:before{ - content:''; - display:block; - position:absolute; - border-radius: 5px; - } - #content #chatList:before{top:17px;left:0px;width:0px;height:98%;box-shadow: 1px 0px 8px 1px #000;} - #content #chatList:after{top:0px;left:17px;width:98%;height:0px;box-shadow: 0px 1px 8px 1px #000;} - - #content #onlineListContainer #onlineList div { - margin: 0 1px 1px 1px; - background: none; - } - #content #onlineListContainer #onlineList ul { - margin-top: 1px; - list-style: none; - } - - #content #helplist table, #content #settingsList table{border-spacing:0;} - #content #emoticonsContainer { border-radius: 3px; } - #content #bbCodeContainer { border: 0; padding-left: 0; } - #content #statusIconContainer { background-image: url('../img/loading-sprite.png'); } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - #content #bbCodeContainer input { - background-color: #d8ffd6; - background-image: -webkit-gradient(linear, left top, left bottom, from(#d8ffd6), to(#467a44)); - background-image: -webkit-linear-gradient(top, #d8ffd6, #467a44); - background-image: -moz-linear-gradient(top, #d8ffd6, #467a44); - background-image: -o-linear-gradient(top, #d8ffd6, #467a44); - background-image: linear-gradient(to bottom, #d8ffd6, #467a44); - color:#1d1d1d; - } - #content #bbCodeContainer input:hover{ - background-color: #ffffff; - background-image: -webkit-gradient(linear, left top, left bottom, from(#ffffff), to(#08ff35)); - background-image: -webkit-linear-gradient(top, #ffffff, #08ff35); - background-image: -moz-linear-gradient(top, #ffffff, #08ff35); - background-image: -o-linear-gradient(top, #ffffff, #08ff35); - background-image: linear-gradient(to bottom, #ffffff, #08ff35); - } - #content #bbCodeContainer input:active, #content #logoutButton:active, #content #submitButton:active, #loginContent #loginButton:active{box-shadow:0px 0px 8px 1px #38ff56;} - #content #logoutButton, #content #submitButton,#loginContent #loginButton{ - background-color: #4fff58; - background-image: -webkit-gradient(linear, left top, left bottom, from(#4fff58), to(#194221)); - background-image: -webkit-linear-gradient(top, #4fff58, #194221); - background-image: -moz-linear-gradient(top, #4fff58, #194221); - background-image: -o-linear-gradient(top, #4fff58, #194221); - background-image: linear-gradient(to bottom, #4fff58, #194221); - font-weight:bold; color:#fff; - text-shadow:0px 0px 5px #000; - } - #content #logoutButton:hover, #content #submitButton:hover, #loginContent #loginButton:hover{ - background-color: #99ffa8; - background-image: -webkit-gradient(linear, left top, left bottom, from(#99ffa8), to(#002405)); - background-image: -webkit-linear-gradient(top, #99ffa8, #002405); - background-image: -moz-linear-gradient(top, #99ffa8, #002405); - background-image: -o-linear-gradient(top, #99ffa8, #002405); - background-image: linear-gradient(to bottom, #99ffa8, #002405); - } +} +.ajax-chat h1, .ajax-chat h3 { + color:#e2e9db; + text-shadow: 1px 1px 2px #191919; +} +.ajax-chat a { + color:#4c9546; +} +.ajax-chat input, .ajax-chat select{ + padding: 3px; +} +#loginContent input, #loginContent select { + background:#171717; + color:#b0b8a8; +} +#content input,#content select,#content textarea { + background-color:#0e0e0e; + color:#b0b8a8; +} +.ajax-chat .popup, #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, +#content #inputFieldContainer, #loginContent input[type=text], #loginContent input[type=password] { + box-shadow: 0px 0px 8px 1px #495a49; + -webkit-box-shadow: 0px 0px 8px 1px #495a49; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + background-color:#171717; + border:0; +} +#content #chatList:after, #content #chatList:before{ + content:''; + display:block; + position:absolute; + border-radius: 5px; +} +#content #chatList:before{ + top:17px; + left:0px; + width:0px; + height:98%; + box-shadow: 1px 0px 8px 1px #000; +} +#content #chatList:after{ + top:0px; + left:17px; + width:98%; + height:0px; + box-shadow: 0px 1px 8px 1px #000; +} + +#content #onlineListContainer #onlineList div { + margin: 0 1px 1px 1px; + background: none; +} +#content #onlineListContainer #onlineList ul { + margin-top: 1px; + list-style: none; +} - #content #colorCodesContainer{ - border:0; - background:rgba(0,0,0,0.7); - padding:7px; - } - #content #colorCodesContainer a{border-radius:10px; box-shadow:0px 0px 3px 1px rgba(255,255,255,0.3); margin:0 5px;border:0;} - #content #optionsContainer input.button { border: 0; padding: 0; margin-right: 5px;} - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#131413; - } - #content .guest { - color:gray; - } - #content .user { - color:#b0b8a8; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#4c9546; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#4c9546; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { +#content #helplist table, #content #settingsList table { + border-spacing:0; +} +#content #emoticonsContainer { + border-radius: 3px; +} +#content #bbCodeContainer { + border: 0; + padding-left: 0; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} + +#content #bbCodeContainer input { + background-color: #d8ffd6; + background-image: -webkit-linear-gradient(top, #d8ffd6, #467a44); + background-image: -moz-linear-gradient(top, #d8ffd6, #467a44); + background-image: -o-linear-gradient(top, #d8ffd6, #467a44); + background-image: linear-gradient(to bottom, #d8ffd6, #467a44); + color:#1d1d1d; +} +#content #bbCodeContainer input:hover{ + background-color: #ffffff; + background-image: -webkit-linear-gradient(top, #ffffff, #08ff35); + background-image: -moz-linear-gradient(top, #ffffff, #08ff35); + background-image: -o-linear-gradient(top, #ffffff, #08ff35); + background-image: linear-gradient(to bottom, #ffffff, #08ff35); +} +#content #bbCodeContainer input:active, #content #logoutButton:active, #content #submitButton:active, #loginContent #loginButton:active{ + box-shadow:0px 0px 8px 1px #38ff56; +} +#content #logoutButton, #content #submitButton,#loginContent #loginButton{ + background-color: #4fff58; + background-image: -webkit-linear-gradient(top, #4fff58, #194221); + background-image: -moz-linear-gradient(top, #4fff58, #194221); + background-image: -o-linear-gradient(top, #4fff58, #194221); + background-image: linear-gradient(to bottom, #4fff58, #194221); + font-weight:bold; color:#fff; + text-shadow:0px 0px 5px #000; +} +#content #logoutButton:hover, #content #submitButton:hover, #loginContent #loginButton:hover{ + background-color: #99ffa8; + background-image: -webkit-linear-gradient(top, #99ffa8, #002405); + background-image: -moz-linear-gradient(top, #99ffa8, #002405); + background-image: -o-linear-gradient(top, #99ffa8, #002405); + background-image: linear-gradient(to bottom, #99ffa8, #002405); +} + +.ajax-chat .popup, .ajax-chat #content .popup#colorCodesContainer { + border:0; + background:rgba(0,0,0,0.7); + padding:5px; +} +#content #colorCodesContainer a{ + border-radius:10px; + box-shadow:0px 0px 3px 1px rgba(255,255,255,0.3); + margin:5px; + border:0; +} +#content #optionsContainer input.button { border: 0; padding: 0; margin-right: 5px;} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#131413; +} +#content #chatList .rowOdd.private, #content #chatList .rowEven.private { + background-color: #0C3A00; + background-image: linear-gradient(to bottom, #1C5417, #05410E); +} +#content .guest { + color:gray; +} +#content .user { + color:#b0b8a8; +} +#content .customUser { + color:#4efb57; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#4c9546; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#4c9546; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { background-color: #171717; - background-image: -webkit-gradient(linear, left top, left bottom, from(#3b6c37), to(#171717)); background-image: -webkit-linear-gradient(top, #3b6c37, #223220, #171717); - background-image: -moz-linear-gradient(top, #3b6c37, #223220, #171717); - background-image: -o-linear-gradient(top, #3b6c37, #223220, #171717); - background-image: linear-gradient(to bottom, #3b6c37, #223220, #171717); - } - + background-image: -moz-linear-gradient(top, #3b6c37, #223220, #171717); + background-image: -o-linear-gradient(top, #3b6c37, #223220, #171717); + background-image: linear-gradient(to bottom, #3b6c37, #223220, #171717); } \ No newline at end of file diff --git a/chat/css/XenForo.css b/chat/css/XenForo.css new file mode 100644 index 0000000..db1a3d0 --- /dev/null +++ b/chat/css/XenForo.css @@ -0,0 +1,238 @@ +/* + * @package AJAX_Chat + * @author ManOnDaMoon + */ +@import url('global.css'); +@import url('fonts.css'); +@import url('print.css'); +@import url('custom.css'); + +/* Main Buttons */ +#content #logoutButton,#content #submitButton,#loginForm #loginButton { + font: 12pt Calibri, 'Trebuchet MS', Verdana, Geneva, Arial, Helvetica, sans-serif; + color: white; + font-weight: bold; + background: #e68c17 url('../../styles/default/xenforo/gradients/form-button-white-25px.png') repeat-x center -7px; + border: 3px solid White; + border-radius: 8px; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + -khtml-border-radius: 8px; + text-align: center; + box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + -khtml-box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.2); + display: inline-block; + cursor: pointer; + height: 33px; + background-color: #e68c17; + padding: 0 10px 0 10px; +} +#content #logoutButton:hover,#content #submitButton:hover,#loginForm #loginButton:hover { + box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + -moz-box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + -khtml-box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); + position: relative; + top: 2px; +} + +/* BBCode Buttons */ +#content #bbCodeContainer input { + font-style: normal; + font-size: 12px; + font-family: Calibri, 'Trebuchet MS', Verdana, Geneva, Arial, Helvetica, sans-serif; + color: rgb(0, 0, 0); + background: rgb(220, 220, 235) url('../../styles/default/xenforo/gradients/form-button-white-25px.png') repeat-x top; + padding: 0px 6px; + border: 1px solid rgb(221, 221, 235); + border-top-color: rgb(255, 255, 255); + border-bottom-color: rgb(179, 179, 189); + border-radius: 7px; + -webkit-border-radius: 7px; + -moz-border-radius: 7px; + -khtml-border-radius: 7px; + text-align: center; + box-shadow: 0px 1px 4px 0px rgb(200, 200, 210); + -webkit-box-shadow: 0px 1px 4px 0px rgb(200, 200, 210); + -moz-box-shadow: 0px 1px 4px 0px rgb(200, 200, 210); + -khtml-box-shadow: 0px 1px 4px 0px rgb(200, 200, 210); + text-shadow: 0 0 0 transparent, 0px -1px 2px white; + outline: none; + line-height: 23px; + display: inline-block; + cursor: pointer; + box-sizing: border-box; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -ms-box-sizing: border-box; + height: 23px; +} +#content #bbCodeContainer input:hover { + color: black; + text-decoration: none; + background-color: rgb(255, 255, 200); + border-color: rgb(255, 255, 200); + border-top-color: white; + border-bottom-color: rgb(190, 190, 170); +} +#content textarea { + font-size: 13px; + font-family: Calibri, 'Trebuchet MS', Verdana, Geneva, Arial, Helvetica, sans-serif; + color: #000000; + background-color: rgb(240, 247, 252); + border-width: 1px; + border-style: solid; + border-top-color: rgb(192, 192, 192); + border-right-color: rgb(233, 233, 233); + border-bottom-color: rgb(233, 233, 233); + border-left-color: rgb(192, 192, 192); + border-radius: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -khtml-border-radius: 4px; + outline: 0; +} +#content textarea:focus { + background: rgb(255, 255, 240) url('../../styles/default/xenforo/gradients/form-element-focus-25.png') repeat-x; + border-top-color: rgb(150, 150, 150); + border-bottom-color: rgb(230, 230, 230); +} +#content select,#loginForm select,#loginForm input { + background-color: #F7F7FF; + color: #60A0DC; + border: 1px solid #a5cae4; +} +#content select:focus,#loginForm select:focus,#loginForm input:focus { + background: rgb(255, 255, 240) url('../../styles/default/xenforo/gradients/form-element-focus-25.png') repeat-x; + border-top-color: rgb(150, 150, 150); + border-bottom-color: rgb(230, 230, 230); +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} + +/* Other Theme Elements */ +.ajax-chat { + background-color: #176093; + color: #F7F7FF; +} +.ajax-chat h1, .ajax-chat a { + color: #60A0DC; +} +#loginContent #errorContainer { + color: red; +} +#content { + background-color: #F7F7FF; + color: #176093; + font-size: 12px; +} +#content #headline { + color:#F7F7FF; + background:#176093; +} +#content #copyright { + color: #F7F7FF; +} +#content #copyright { + font-style: italic; + font-size: 0.8em; +} +#content #chatList,#content #colorCodesContainer { + background-color: rgb(240, 247, 252); + border: 1px solid #a5cae4; + border-radius: 10px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + -khtml-border-radius: 10px; +} +#content #onlineListContainer,#content #helpContainer,#content #settingsContainer, #content #reportContainer { + background-color: rgb(240, 247, 252); + border: 1px solid #a5cae4; + border-radius: 10px; + -webkit-border-radius: 10px; + -moz-border-radius: 10px; + -khtml-border-radius: 10px; +} +.ajax-chat .popup { + background-color:#FFF; + border: 1px solid #ababab; + box-shadow: 2px 2px 2px #777; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content #onlineListContainer #onlineList, #content #helpContainer #helpList, #content #settingsContainer #settingsList, #content #reportContainer #reportList { + padding-top: 5px; +} +#content #bbCodeContainer,#content #emoticonsContainer { + background-color: rgb(240, 247, 252); + padding: 5px; +} +#content #colorCodesContainer a { + border-color: #a5cae4; +} +#content #optionsContainer input { + background-color: transparent; +} +#content .rowEven { + background: #d7edfc url('../../styles/default/xenforo/gradients/category-23px-light.png') repeat-x top; +} +#content .rowOdd { + background-color: #F7F7FF; +} +#content .guest { + color: gray; +} +#content .user { + color: #176093; +} +#content .customUser { + color: #e68c17; +} +#content .moderator { + color: #00AA00; +} +#content .admin, #content .chatBotErrorMessage { + color: red; +} +#content .chatBot { + color: #60A0DC; +} +#content #chatList a { + color: #60A0DC; +} +#content #chatList .deleteSelected { + border-color: red; +} +#content #onlineListContainer h3,#content #helpContainer h3,#content #settingsContainer h3,#content #reportContainer h3 { + font-size: 11px; + background: #f9d9b0 url('../../styles/default/xenforo/gradients/category-23px-light.png') repeat-x top; + color: #6d3f03; + border-bottom: 1px solid #f9bc6d; +} +#content #chatList .report { + display:block; + float:right; + width:10px; + height:10px; + margin-top:2px; + margin-left:5px; + background:url('../img/report.gif') 0 0 no-repeat; +} +#content #chatList .report:hover { + background-position:0 -10px;; +} diff --git a/chat/css/beige.css b/chat/css/beige.css index 19f4771..ca99df3 100644 --- a/chat/css/beige.css +++ b/chat/css/beige.css @@ -2,114 +2,110 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#d9ce72; + color:#333333; + border:1px solid #c8b360; + background-image:linear-gradient(to bottom, #d9ce72, #e1d995); +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-image:linear-gradient(to bottom, #C1B022, #C8BD66); +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border:1px solid #c8b360; +} + +/* Status Icon */ +#content #statusIconContainer { + background:url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#d9ce72; - color:#333333; - border: 1px solid #c8b360; - background-image: linear-gradient(to bottom, #d9ce72, #e1d995); - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #c8b360; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#F7F5DC; - color:#000; - } - #loginContent h1 { - color:#000; - } - #loginContent a { - color:#000; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#000; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#eee9be; - color:#000; - } - #content h1 { - color:#000; - } - #content a { - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:gray; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#FFFFF0; - } - #content .rowOdd { - background-color:#F7F5DC; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#FF6600; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#1E90FF; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#FFFFF0; - color:#000; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#F7F5DC; + color:#000; +} +.ajax-chat a { + color:#000; +} +.ajax-chat h1 { + color:#000; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#000; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:gray; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#F7F5DC; + border: 1px solid #808080; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#FFFFF0; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#F7F5DC; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#645a30; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#FF6600; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#1E90FF; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#FFFFF0; + color:#000; } \ No newline at end of file diff --git a/chat/css/black.css b/chat/css/black.css index 7a9d12d..2420ec9 100644 --- a/chat/css/black.css +++ b/chat/css/black.css @@ -2,115 +2,115 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ + * + * Color palette inspired by PunBB style "Cobalt": + * http://punbb.org/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#000; + color:#f0f0f0; + border: 1px solid #808080; + background-image: linear-gradient(to bottom, #222, #000); +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-color:#222; + background-image: linear-gradient(to top, #222, #000); +} +#content select, #loginForm select, #loginForm input, #content textarea { + background-color:#000; + color:#fafafa; + border: 1px solid #808080; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#000; - color:#f0f0f0; - border: 1px solid #808080; - background-image: linear-gradient(to bottom, #222, #000); - } - #content select, #loginForm select, #loginForm input, #content textarea { - background-color:#000; - color:#fafafa; - border: 1px solid #808080; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#000; - color:#FFF; - } - #loginContent h1 { - color:#FFF; - } - #loginContent a { - color:#FFF; - } - #loginContent input, #loginContent select { - background-color:#212121; - color:#FFF; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#000; - color:#FFF; - } - #content h1 { - color:#FFF; - } - #content a { - color:#FFF; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:gray; - background-color:#000; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#212121; - } - #content .rowOdd { - background-color:#000; - } - #content .guest { - color:gray; - } - #content .user { - color:#FFF; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#FF6600; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#1E90FF; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#212121; - color:#FFF; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#000; + color:#FFF; +} +.ajax-chat a { + color:#FFF; +} +.ajax-chat h1 { + color:#FFF; +} +#loginContent input, #loginContent select { + background-color:#212121; + color:#FFF; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:gray; + background-color:#000; +} +.ajax-chat .popup { + background-color:#000; + border: 1px solid gray; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#212121; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#000; +} +#content #chatList .rowOdd.private { + background-color:#B83C1D; +} +#content #chatList .rowEven.private { + background-color:#C84A24; +} +#content .guest { + color:gray; +} +#content .user { + color:#FFF; +} +#content .customUser { + color:#ffcc33; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#FF6600; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#1E90FF; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#212121; + color:#FFF; } \ No newline at end of file diff --git a/chat/css/custom.css b/chat/css/custom.css index ab4a1db..63fdec0 100644 --- a/chat/css/custom.css +++ b/chat/css/custom.css @@ -1,36 +1,17 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - */ - /* Custom CSS - enter css overrides for all styles below */ -/* e.g. suppose you want to hide the logout button and the channel / style / language selectors + +/* e.g. to hide the logout button and the channel, style and language selectors, uncomment the following: + #content #logoutChannelContainer { display:none; } -#content #chatList { - top:66px; -} -#content #onlineListContainer { - top:66px; +#content #mainPanelContainer { + top: 45px; } #content #copyright { - right:50px; - top:18px; + right: 60px; } #content #statusIconContainer { - right:12px; - top:12px; + top: 10px; } -#content #helpContainer { - top:66px; -} -#content #settingsContainer { - top:66px; -} - - */ +*/ \ No newline at end of file diff --git a/chat/css/fonts.css b/chat/css/fonts.css index e12f628..be935ca 100644 --- a/chat/css/fonts.css +++ b/chat/css/fonts.css @@ -1,12 +1,3 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - */ - -/* Fonts */ #loginContent { font-family:Verdana, Arial, Helvetica, sans-serif; font-size:0.8em; @@ -58,6 +49,9 @@ #content #chatList span.user { font-weight:bold; } +#content #chatList span.customUser { + font-weight:bold; +} #content #chatList span.moderator { font-weight:bold; } @@ -89,12 +83,9 @@ #content #onlineListContainer #onlineList div { font-size:0.9em; } -#content #helpContainer #helpList td { +#content #helpContainer #helpList, #content #settingsContainer #settingsList { font-size:0.9em; } -#content #helpContainer #helpList td.code { +#content #helpContainer #helpList dd { font-style:italic; -} -#content #settingsContainer #settingsList td { - font-size:0.9em; } \ No newline at end of file diff --git a/chat/css/global.css b/chat/css/global.css index dbfa02d..83ce0cb 100644 --- a/chat/css/global.css +++ b/chat/css/global.css @@ -8,25 +8,28 @@ */ /* Positioning */ -#loginContent { - position:absolute; - width:100%; - height:100%; +body.ajax-chat { + padding:0; + margin:0; } -#loginContent #loginHeadlineContainer { - margin: 100px 100px 0 100px; +#loginContent { + margin: 0 auto 0; + padding-top: 20px; + width:250px; } -#loginContent #loginFormContainer, #loginContent #errorContainer { - margin: 0 100px; +#loginContent #loginHeadline { + margin:0; + line-height:35px; } -#loginContent #loginFormContainer div { - margin-bottom:7px; +#loginContent #loginForm div { + margin:5px 0; } #loginContent #loginRegisteredUsers { padding-top:5px; } #loginContent #copyright { - margin: 20px 100px 0 100px; + margin:20px 0 0 0; + text-align:right; } #content { position:absolute; @@ -36,99 +39,136 @@ #content #copyright { position:absolute; right:20px; - top:18px; + top:20px; } -#content #headlineContainer { +#content #headline { position:absolute; - left:20px; - top:5px; + left:0px; + right:0px; + top:0px; + padding:0 0 0 20px; + line-height:45px; + margin:0; } #content #logoutChannelContainer { position:absolute; + z-index:2; left:20px; top:50px; + right:50px; } #content #logoutChannelContainer select{ - width: 105px; - height: 22px; + width:105px; + height:22px; } #content #statusIconContainer { position:absolute; right:20px; top:50px; - width: 22px; - height: 22px; + width:22px; + height:22px; } #content #chatList { - position:absolute; - left:20px; - right:230px; - top:85px; - bottom:150px; + height:100%; overflow:auto; } -#content #inputFieldContainer { +#content #mainPanelContainer { position:absolute; left:20px; right:20px; - bottom:90px; - padding-right:4px; + top:85px; + bottom:150px; } -#content #submitButtonContainer { - position:absolute; - right:20px; - bottom:54px; +#content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + float:right; + height:100%; + overflow:auto; + margin-left:10px; + position:relative; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + line-height:25px; + padding: 0 10px; + margin:0; + text-align:center; } #content #onlineListContainer { - position:absolute; - right:20px; - top:85px; width:200px; - bottom:150px; } -#content #helpContainer { +#content #helpContainer, #content #settingsContainer { + width:360px; +} +#content #onlineListContainer #onlineList, #content #helpContainer #helpList, #content #settingsContainer #settingsList { position:absolute; + left:0px; + right:0px; + top:25px; + bottom:0px; + overflow:auto; +} +#content #helpContainer #helpList dl, #content #settingsContainer #settingsList dl { + overflow:hidden; + padding:5px; + margin:0; +} +#content #helpContainer #helpList dt, #content #settingsContainer #settingsList dt { + float:left; + width:55%; + padding:0 2% 0 0; + margin:0; + clear:left; +} +#content #helpContainer #helpList dd, #content #settingsContainer #settingsList dd { + width:43%; + float:left; + margin:0; + padding:0; +} +#content #inputFieldContainer { + position:absolute; + left:20px; right:20px; - top:85px; - width:360px; - bottom:150px; + bottom:95px; + padding-right:2px; +} +#content #inputFieldContainer #inputField { + width:100%; + height:40px; + padding:0; } -#content #settingsContainer { +#content #submitButtonContainer { position:absolute; right:20px; - top:85px; - width:360px; - bottom:150px; + bottom:60px; } #content #bbCodeContainer { position:absolute; left:20px; bottom:20px; padding:3px; - padding-left: 0px; } -#content #colorCodesContainer { +#content #bbCodeContainer input { + display:block; + float:left; + margin-right:2px; +} +#content #bbCodeContainer #bbCodeEmoticon { + display:none; +} +#content #colorCodesContainer, #content #emoticonsContainer { position:absolute; left:20px; bottom:55px; padding:3px; z-index:1; } -#content #emoticonsContainer { - position:absolute; - left:20px; - bottom:57px; - padding:3px; -} -#content #optionsContainer { - position:absolute; - right:20px; - bottom:24px; - padding:3px; - padding-right:0px; +#content #emoticonsContainer a { + margin-left:1px; + margin-right:1px; } -#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginContent #loginButton { - padding: 4px 10px; +#content #emoticonsContainer img { + vertical-align:middle; + margin-bottom:2px; } #content #colorCodesContainer a { display:block; @@ -136,24 +176,22 @@ width:20px; height:20px; } +#content #optionsContainer { + position:absolute; + right:20px; + bottom:20px; + padding:3px 0 3px 3px; +} #content #optionsContainer input { vertical-align:middle; } -#content #optionsContainer input.button { - width:22px; - height:22px; -} -#content #emoticonsContainer a { - margin-left:1px; - margin-right:1px; -} -#content #emoticonsContainer img { - vertical-align:middle; - margin-bottom:2px; +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginContent #loginButton { + padding: 4px 10px; } -#content #headlineContainer h1 { - margin-left:auto; - margin-top:12px; +#content #logoutButton { + display:block; + float:left; + margin: 0 5px 5px 0; } #content #chatList div { padding: 2px 10px; @@ -179,24 +217,6 @@ padding-left:5px; background:url('../img/delete.png') no-repeat right; } -#content #inputFieldContainer #inputField { - width:100%; - height:40px; -} -#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - height:30px; - padding: 4px 10px; - margin:0px; - text-align:center; -} -#content #onlineListContainer #onlineList, #content #helpContainer #helpList, #content #settingsContainer #settingsList { - position:absolute; - left:0px; - right:0px; - top:25px; - bottom:0px; - overflow:auto; -} #content #onlineListContainer #onlineList div { padding: 2px 10px; } @@ -207,61 +227,56 @@ margin: 5px 0; padding-left:20px; } -#content #helpContainer #helpList td, #content #settingsContainer #settingsList td { - padding: 4px 10px; - vertical-align:top; -} -#content #settingsContainer #settingsList td { - vertical-align:middle; -} -#content #settingsContainer #settingsList td.setting { - width:115px; -} -#content #settingsContainer #settingsList input.text { - width:100px; -} #content #settingsContainer #settingsList select.left { text-align:right; } -#content #settingsContainer #settingsList input.button { - width:22px; - height:22px; - vertical-align:middle; - margin-bottom:2px; -} /* Buttons */ +#content #optionsContainer input, #content #settingsContainer #settingsList input.button, #content #showChannelsButton { + background: url('../img/buttons-sprite.png') no-repeat; + vertical-align:middle; + width:22px; + height:22px; + padding:0; +} #content #optionsContainer #helpButton { - background:url('../img/help.png') no-repeat; + background-position: -69px -22px; } #content #optionsContainer #settingsButton { - background:url('../img/settings.png') no-repeat; + background-position: -92px 0px; } #content #optionsContainer #onlineListButton { - background:url('../img/users.png') no-repeat; + background-position: -69px 0px; } #content #optionsContainer #audioButton { - background:url('../img/audio.png') no-repeat 0px 0px; + background-position: 0px 0px; } #content #optionsContainer #audioButton.off { - background-position: 0px 100%; + background-position: 0px -22px; } #content #optionsContainer #autoScrollButton { - background:url('../img/autoscroll.png') no-repeat 0px 0px; + background-position: -23px 0px; } #content #optionsContainer #autoScrollButton.off { - background-position: 0px 100%; + background-position: -23px -22px; } #content #settingsContainer #settingsList input.playback { - background:url('../img/playback.png') no-repeat; + background-position: -92px -22px; + margin-bottom:3px; +} +#content #logoutChannelContainer #showChannelsButton { + background-position: -46px 0px; + display:none; +} +#content #logoutChannelContainer #showChannelsButton.off { + background-position: -46px -22px; } /* Borders */ #content img { border:none; } -#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, -#content #colorCodesContainer a, #content textarea { +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer a, #content textarea { border-width:1px; border-style:solid; } @@ -269,11 +284,46 @@ border-width:1px; border-style:dotted; } -#content #helpContainer #helpList table, #content #settingsContainer #settingsList table { - border-collapse:collapse; -} /* Misc */ #content #bbCodeContainer input, #content #optionsContainer input.button, #content #settingsContainer #settingsList input.button, #content #logoutButton, #content #submitButton, #loginContent #loginButton { cursor:pointer; +} + +@media (max-width: 700px) { + #content #mainPanelContainer { top:65px; left:5px; right:5px; bottom:105px; } + #content #headline { margin:0; padding:0 0 0 5px; line-height:25px; } + #content #statusIconContainer { top:2px; right:2px; } + #content #copyright { top:0px; right:30px; line-height: 25px;} + #content #logoutChannelContainer { top:30px; left:5px; right:0px; } + #content #logoutChannelContainer label { display:none; } + #content #emoticonsContainer, #content #submitButtonContainer { display:none; } + #content #bbCodeContainer { bottom:45px; left:5px; padding:0; } + #content #bbCodeContainer #bbCodeEmoticon { display:block; } + #content #bbCodeContainer #bbCodeQuote, #content #bbCodeContainer #bbCodeCode { display:none; } + #content #optionsContainer { bottom:10px; right:5px; } + #content #chatList { left:5px; right:150px; bottom:115px; font-size:12px; } + #content #inputFieldContainer #inputField { height:20px; } + #content #inputFieldContainer { left:5px; right:5px; bottom:75px; } + #content #colorCodesContainer, #content #emoticonsContainer { bottom:85px; left:5px; } + #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { margin-left:0; border-left-width:0; } +} + +@media (max-width: 480px) { + #content #mainPanelContainer { top:65px; left:0px; right:0px; bottom:105px; } + #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { border-right-width:0; position:absolute; width:auto; top:0px; right:0px; bottom:0px; left:0px; } + #content #chatList { border-left-width: 0; border-right-width: 0; } + #content #bbCodeContainer #bbCodeIMG, #content #bbCodeContainer #bbCodeURL { display:none; } + #content #bbCodeContainer { padding:0; } + #content #emoticonsContainer { bottom:80px; left:5px; } + #content #logoutChannelInner { display:none; clear:left; padding:5px; text-align:right; } + #content #logoutChannelContainer #showChannelsButton { display:block; position:absolute; right:5px; top:5px; } +} + +@media (max-height: 300px) { + #content #inputFieldContainer #inputField { height:20px; } + #content #bbCodeContainer, #content #optionsContainer, #content #emoticonsContainer, #content #submitButtonContainer, + #content #statusIconContainer, #content #logoutChannelContainer { display:none; } + #content #inputFieldContainer { bottom: 5px; } + #content #mainPanelContainer { bottom:40px; top:35px; } } \ No newline at end of file diff --git a/chat/css/grey.css b/chat/css/grey.css index 7d58380..64b70c4 100644 --- a/chat/css/grey.css +++ b/chat/css/grey.css @@ -2,115 +2,110 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#8a8a8a; + color:#fff; + border:1px solid #808080; + background-image:linear-gradient(to bottom, #8a8a8a, #444); +} +#content #bbCodeContainer input:hover, #content #logoutButton:hover, #content #submitButton:hover, #loginForm #loginButton:hover { + background-image:linear-gradient(to top, #8a8a8a, #444); +} +#content select, #loginForm select, #loginForm input, #content textarea { + color:#333333; + border: 1px solid #808080; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#8a8a8a; - color:#fff; - border: 1px solid #808080; - background-image: linear-gradient(to bottom, #8a8a8a, #444); - } - #content select, #loginForm select, #loginForm input, #content textarea { - color:#333333; - border: 1px solid #808080; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Other Theme Elements */ - #loginContent { - background-color:#F6F6F6; - color:#000; - } - #loginContent h1 { - color:#000; - } - #loginContent a { - color:#000; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#000; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#d0d0d0; - color:#000; - } - #content h1 { - color:#000; - } - #content a { - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea { - border-color:gray; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#FFF; - } - #content .rowOdd { - background-color:#F6F6F6; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#FF6600; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#1E90FF; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#FFF; - color:#000; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#d0d0d0; + color:#000; +} +.ajax-chat h1 { + color:#000; +} +.ajax-chat a { + color:#000; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#000; +} +#loginContent #errorContainer { + color:red; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content textarea { + border-color:gray; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#FFF; + border:1px solid gray; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#FFF; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#F6F6F6; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#606060; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#FF6600; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#1E90FF; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#FFF; + color:#000; } \ No newline at end of file diff --git a/chat/css/ie5-6.css b/chat/css/ie5-6.css deleted file mode 100644 index c6ddda3..0000000 --- a/chat/css/ie5-6.css +++ /dev/null @@ -1,70 +0,0 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - */ - - -/* - * Positioning adjustments for IE versions < 7 - */ - -body { - width:100%; - height:100%; -} -#content #chatList { - position:static; - margin-right:230px; - margin-left:20px; - margin-top:85px; - height:360px; -} -#content #onlineListContainer { - height:360px; -} -#content #helpContainer { - height:360px; -} -#content #settingsContainer { - height:360px; -} -#content #inputFieldContainer { - top:460px; - padding:0px; -} -#content #submitButtonContainer { - top:517px; -} -#content #bbCodeContainer { - top:550px; -} -#content #colorCodesContainer { - top:516px; -} -#content #emoticonsContainer { - top:517px; -} -#content #optionsContainer { - top:555px; -} -#content #inputFieldContainer #inputField { - width:94%; -} -#content #onlineListContainer #onlineList { - width:100%; - height:335px; - overflow:auto; -} -#content #helpContainer #helpList { - width:100%; - height:335px; - overflow:auto; -} -#content #settingsContainer #settingsList { - width:100%; - height:335px; - overflow:auto; -} \ No newline at end of file diff --git a/chat/css/pine_images/chatlist.jpg b/chat/css/pine_images/chatlist.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e7366dfe13ec0801c7e069867cb2b2c721d723f3 GIT binary patch literal 50564 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 RbSY+d?il><_2Qr9{|g+;-If3V literal 0 HcmV?d00001 diff --git a/chat/css/print.css b/chat/css/print.css index a37857d..5a5516e 100644 --- a/chat/css/print.css +++ b/chat/css/print.css @@ -1,25 +1,11 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - */ - - -/* - * Print layout - */ - @media print { - #content { position:static; } #content #copyright { display:none; } - #content #headlineContainer { + #content #headline { display:none; } #content #logoutChannelContainer { @@ -72,11 +58,9 @@ #content #chatList .delete { display:none; } - #content #chatList { border:none; } - #content { font-family:'times new roman', times, serif; font-size:1.0em; @@ -85,7 +69,6 @@ #content #chatList code { font-size:0.8em; } - #content { color:#000; } @@ -95,6 +78,9 @@ #content .user { color:#000; } + #content .customUser { + color:#0044CC; + } #content .moderator { color:#00AA00; } @@ -110,5 +96,4 @@ #content #chatList a { color:#1E90FF; } - } \ No newline at end of file diff --git a/chat/css/prosilver.css b/chat/css/prosilver.css index 76ba6dd..f0a0c32 100644 --- a/chat/css/prosilver.css +++ b/chat/css/prosilver.css @@ -2,154 +2,150 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by phpBB style "prosilver": * http://www.phpbb.com/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Firefox button padding fix */ +#content #bbCodeContainer input::-moz-focus-inner, #content #logoutButton::-moz-focus-inner, #content #submitButton::-moz-focus-inner { + border:0; + padding:0; +} + +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#F7F5F1; + color:#333333; + border: 1px solid #8a8a8a; + background-image: linear-gradient(to bottom, #fafafa, #cdcdcd); + background-image: -webkit-linear-gradient(top, #fafafa, #cdcdcd); +} +#content select, #loginForm select, #loginForm input, #content textarea { + background-color:#FFF; + color:#333333; + border: 1px solid #ababab; +} + +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} + +/* Headers */ +#loginContent h1 { + color:#333333; +} +#content #headline { + left: 0px; + right: 0px; + background: #0c95d9; + color: white; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#0c95d9; + color:#fff; + height: 25px; +} - /* Firefox button padding fix */ - #content #bbCodeContainer input::-moz-focus-inner, #content #logoutButton::-moz-focus-inner, #content #submitButton::-moz-focus-inner { - border:0; - padding:0; - } - - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#F7F5F1; - color:#333333; - border: 1px solid #8a8a8a; - background-image: linear-gradient(to bottom, #fafafa, #cdcdcd); - background-image: -webkit-linear-gradient(top, #fafafa, #cdcdcd); - } - #content select, #loginForm select, #loginForm input, #content textarea { - background-color:#FFF; - color:#333333; - border: 1px solid #ababab; - } - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - /* Headers */ - #loginContent h1 { - color:#333333; - } - #content #headlineContainer h1 { - color: white; - margin-left: 20px; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#0c95d9; - color:#fff; - height: 25px; - } - - /* Other Theme Elements */ - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer { - border-color: #ababab; - } - #content #chatList .deleteSelected { - border-width:1px; - border-style:dotted; - } - #content #helpContainer #helpList table, #content #settingsContainer #settingsList table { - border-collapse:collapse; - } - #loginContent { - background-color:#F9F9F9; - color:#28313F; - } - #loginContent a { - color:#333333; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#333333; - } - #loginContent #loginFormContainer #loginButton { - background-color:#F7F5F1; - color:#333333; - } - #loginContent #errorContainer { - color:red; - } - #content #headlineContainer { - top: 0; - left: 0; - width: 100%; - background: #0c95d9; - } - #content #copyright, #content #copyright a { - color: white; - } - #content { - background-color:#F9F9F9; - color:#28313F; - } - #content a { - color:#333333; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #emoticonsContainer { - background-color:#FFF; - } - #content #colorCodesContainer { - box-shadow: 2px 2px 2px #777; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#eaf1f6; - } - #content .rowOdd { - background-color:#e1eaf2; - } - #content .guest { - color:gray; - } - #content .user { - color:#333333; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:#AA0000; - } - #content .chatBot { - color:#D31141; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#D31141; - } - #content #chatList .deleteSelected { - border-color:red; - } +/* Other Theme Elements */ +.ajax-chat { + background-color:#F9F9F9; + color:#28313F; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + border-color: #ababab; +} +.ajax-chat .popup { + background-color:#FFF; + border: 1px solid #ababab; + box-shadow: 2px 2px 2px #777; +} +#content #chatList .deleteSelected { + border-width:1px; + border-style:dotted; +} +#content #helpContainer #helpList table, #content #settingsContainer #settingsList table { + border-collapse:collapse; +} +#loginContent a { + color:#333333; +} +#loginContent input, #loginContent select { + background-color:#FFF; + color:#333333; +} +#loginContent #loginButton { + background-color:#F7F5F1; + color:#333333; +} +#loginContent #errorContainer { + color:red; +} +#content #copyright, #content #copyright a { + color: white; +} +#content a { + color:#333333; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content #emoticonsContainer { + background-color:#FFF; +} +#content #colorCodesContainer { + box-shadow: 2px 2px 2px #777; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#eaf1f6; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#e1eaf2; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#333333; +} +#content .customUser { + color:#0c95d9; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:#AA0000; +} +#content .chatBot { + color:#D31141; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#D31141; +} +#content #chatList .deleteSelected { + border-color:red; } \ No newline at end of file diff --git a/chat/css/shoutbox.css b/chat/css/shoutbox.css index d22a92a..1b7a634 100644 --- a/chat/css/shoutbox.css +++ b/chat/css/shoutbox.css @@ -90,6 +90,10 @@ font-size:0.9em; font-weight:bold; } +#ajaxChatContent #ajaxChatChatList span.customUser { + font-size:0.9em; + font-weight:bold; +} #ajaxChatContent #ajaxChatChatList span.moderator { font-size:0.9em; font-weight:bold; diff --git a/chat/css/subSilver.css b/chat/css/subSilver.css deleted file mode 100644 index f105b41..0000000 --- a/chat/css/subSilver.css +++ /dev/null @@ -1,118 +0,0 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - * - * Color palette inspired by phpBB style "subSilver": - * http://www.phpbb.com/ - */ - -@import url('global.css'); -@import url('fonts.css'); -@import url('print.css'); -@import url('custom.css'); - -@media screen,projection,handheld { - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - #loginContent { - background-color:#E5E5E5; - color:#000; - } - #loginContent h1 { - color:#006699; - } - #loginContent a { - color:#000; - } - #loginContent input, #loginContent select { - background-color:#FFF; - color:#000; - } - #loginContent #loginFormContainer #loginButton { - background-color:#F7F5F1; - color:#000; - } - #loginContent #errorContainer { - color:red; - } - - #content { - background-color:#E5E5E5; - color:#000; - } - #content h1 { - color:#006699; - } - #content a { - color:#000; - } - #content input, #content select, #content textarea { - background-color:#FFF; - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer, #content textarea{ - border-color:#006699; - background-color:#FFF; - } - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton { - background-color:#F7F5F1; - color:#000; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#DEE3E7; - } - #content .rowOdd { - background-color:#EFEFEF; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#006600; - } - #content .admin { - color:#FFA34F; - } - #content .chatBot { - color:#DD6900; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#006699; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#DEE3E7; - color:#006699; - } -} \ No newline at end of file diff --git a/chat/css/subblack2.css b/chat/css/subblack2.css deleted file mode 100644 index c1cb0db..0000000 --- a/chat/css/subblack2.css +++ /dev/null @@ -1,118 +0,0 @@ -/* - * @package AJAX_Chat - * @author Sebastian Tschan - * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ - * - * Color palette inspired by phpBB style "subblack2": - * http://www.phpbb.com/ - */ - -@import url('global.css'); -@import url('fonts.css'); -@import url('print.css'); -@import url('custom.css'); - -@media screen,projection,handheld { - - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - #loginContent { - background-color:#000; - color:#FFFFCC; - } - #loginContent h1 { - color:#FFFFCC; - } - #loginContent a { - color:#FFFFCC; - } - #loginContent input, #loginContent select { - background-color:#212121; - color:#FFFFCC; - } - #loginContent #loginFormContainer #loginButton { - background-color:#212121; - color:#FFFFCC; - } - #loginContent #errorContainer { - color:red; - } - - #content { - background-color:#000; - color:#FFFFCC; - } - #content h1 { - color:#CC9900; - } - #content a { - color:#FFFFCC; - } - #content input, #content select, #content textarea { - background-color:#212121; - color:#FFFFCC; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #bbCodeContainer, #content #colorCodesContainer, #content #emoticonsContainer { - border-color:gray; - background-color:#212121; - } - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton { - background-color:#212121; - color:#FFFFCC; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#212121; - } - #content .rowOdd { - background-color:#000; - } - #content .guest { - color:gray; - } - #content .user { - color:#FFFFCC; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#CC9900; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#FFCC00; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#212121; - color:#FFCC00; - } -} \ No newline at end of file diff --git a/chat/css/vBulletin.css b/chat/css/vBulletin.css index b8816c2..43998a4 100644 --- a/chat/css/vBulletin.css +++ b/chat/css/vBulletin.css @@ -2,127 +2,130 @@ * @package AJAX_Chat * @author Sebastian Tschan * @author Philip Nicolcev - * @copyright (c) Sebastian Tschan - * @license Modified MIT License - * @link https://blueimp.net/ajax/ * * Color palette inspired by vBulletin style "Standard-Style": * http://www.vbulletin.com/ */ - @import url('global.css'); @import url('fonts.css'); @import url('print.css'); @import url('custom.css'); -@media screen,projection,handheld { +/* Buttons */ +#content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { + background-color:#7192A8; + color:#fff; + border: 0; +} +#content select, #loginForm select, #loginForm input, #content textarea { + background-color:#7192A8; + color:#fff; + border: 1px solid #2F4456; +} - /* Buttons */ - #content #bbCodeContainer input, #content #logoutButton, #content #submitButton, #loginForm #loginButton { - background-color:#7192A8; - color:#fff; - border: 0; - } - #content select, #loginForm select, #loginForm input, #content textarea { - background-color:#7192A8; - color:#fff; - border: 1px solid #2F4456; - } +/* Status Icon */ +#content #statusIconContainer { + background: url('../img/loading-sprite.png') no-repeat 0px 0px; +} +#content #statusIconContainer.waiting { + background-position: 0px -22px; +} +#content #statusIconContainer.retrying { + background-position: 0px -44px; +} - /* Status Icon */ - #content #statusIconContainer { - background-image: url('../img/loading-sprite.png'); - } - #content .statusContainerOff { - background-position: 0px 0px; - } - #content .statusContainerOn { - background-position: 0px -22px; - } - #content .statusContainerAlert { - background-position: 0px -44px; - } - - #loginContent { - background-color:#E1E1E2; - color:#000; - } - #loginContent h1 { - color:#3B5485; - } - #loginContent a { - color:#3B5485; - } - #loginContent #errorContainer { - color:red; - } - #content { - background-color:#E1E1E2; - color:#000; - } - #content #headlineContainer h1 { - color:#fff; - margin-left: 20px; - } - #content #headlineContainer { - top: 0; - left: 10px; - right: 10px; - background: #2F4456; - border-radius: 0px 0px 5px 5px; - } - #content #copyright, #content #copyright a { - color: white; - } - #content a { - color:#3B5485; - } - #content input, #content select, #content textarea { - background-color:#FFF; - color:#000; - } - #content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer, #content #colorCodesContainer { - border-color:#0B198C; - background-color:#FFF; - } - #content #colorCodesContainer a { - border-color:black; - } - #content #optionsContainer input { - background-color:transparent; - } - #content .rowEven { - background-color:#E1E4F2; - } - #content .rowOdd { - background-color:#F5F5FF; - } - #content .guest { - color:gray; - } - #content .user { - color:#000; - } - #content .moderator { - color:#00AA00; - } - #content .admin { - color:red; - } - #content .chatBot { - color:#3B5485; - } - #content #chatList .chatBotErrorMessage { - color:red; - } - #content #chatList a { - color:#3B5485; - } - #content #chatList .deleteSelected { - border-color:red; - } - #content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { - background-color:#7192A8; - color:#FFF; +/* Other Theme Elements */ +.ajax-chat { + background-color:#E1E1E2; + color:#000; +} +.ajax-chat h1 { + color:#3B5485; +} +.ajax-chat a { + color:#3B5485; +} +#loginContent #errorContainer { + color:red; +} +#content #headline { + color:#fff; + padding-left: 20px; + top:0; + left:10px; + right:10px; + background:#2F4456; + border-radius: 0px 0px 5px 5px; +} +#content #copyright, #content #copyright a { + color:white; +} +#content input, #content select, #content textarea { + background-color:#FFF; + color:#000; +} +#content #chatList, #content #onlineListContainer, #content #helpContainer, #content #settingsContainer { + border-color:#0B198C; + background-color:#FFF; +} +.ajax-chat .popup { + background-color:#FFF; + border:1px solid #0B198C; +} +#content #colorCodesContainer a { + border-color:black; +} +#content #optionsContainer input { + background-color:transparent; +} +#content .rowEven, #helpList dl:nth-child(even), #settingsList dl:nth-child(even) { + background-color:#E1E4F2; +} +#content .rowOdd, #helpList dl:nth-child(odd), #settingsList dl:nth-child(odd) { + background-color:#F5F5FF; +} +#content #chatList .rowOdd.private { + background-color:#F8D0D0; +} +#content #chatList .rowEven.private { + background-color:#F8D9D0; +} +#content .guest { + color:gray; +} +#content .user { + color:#000; +} +#content .customUser { + color:#0b198c; +} +#content .moderator { + color:#00AA00; +} +#content .admin { + color:red; +} +#content .chatBot { + color:#3B5485; +} +#content #chatList .chatBotErrorMessage { + color:red; +} +#content #chatList a { + color:#3B5485; +} +#content #chatList .deleteSelected { + border-color:red; +} +#content #onlineListContainer h3, #content #helpContainer h3, #content #settingsContainer h3 { + background-color:#7192A8; + color:#FFF; +} + +@media (max-width: 700px) { + #content #headline { + left:5px; + right:5px; + padding-left:10px; } } \ No newline at end of file diff --git a/chat/img/audio.png b/chat/img/audio.png deleted file mode 100644 index fb37053165d0335cc899c6a9eefe2e059be2dd42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3966 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 Y0QrW-VE_OC diff --git a/chat/img/autoscroll.png b/chat/img/autoscroll.png deleted file mode 100644 index 7c313e5973191c7a047f3eb866b1250c07f416da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4181 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 diff --git a/chat/img/buttons-sprite.png b/chat/img/buttons-sprite.png new file mode 100644 index 0000000000000000000000000000000000000000..c7b3cb59c72110fbc0b48d0054d794a65df1d9c1 GIT binary patch literal 8327 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 Rj6eVY002ovPDHLkV1hz8a+v@C diff --git a/chat/img/playback.png b/chat/img/playback.png deleted file mode 100644 index 10102d829cd4a82c14aa3bd7d3a56a2f5c35dff0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 961 zcmV;y13vtTP)b z)!%CrR}=^E@41uN*~~861ncT5ZIj(it&JE<-zq{O)DUb7hFW9Pg7u~9i}(vc1O>s` zE*NWDY8zr&v;FZ8l-efo7otg4Z9yyOQz@BEvYTXflkDuwj&pp7yX@u{snr7q?tPfg z+!me;?&YUSCk-@7}AC<$Pc7upnNE+l|g zH@9rcGBaB_i__z2KJ#U((XzEUzV7X{I{^d%_==yIE9ZmgsCO19%01`qc2?u*O4)I8RKDvu)da=dU}?j*maeG<~o!8VCg51h5Q%Yc{x~ z#%{NJX8jnL%uK+@8hCBR3Oe^qTlvMV-!>_3<-L@yM|yAc|LpVm#)J@~x~@Mew5qrl zwY9aO)oWJW`ziX1k{nHdo;0(h+mZ}b^}sSsw0E={y?r+mW69KEP19})Ax4QP9gD@d zWW}LS$jOf-NdkdNU}lE>D2Fk91TI;@u1|KCH*IWs`OFusZHr#4?9WVQ_Y=_ofI(3V z2ZKQ;59MrYDsxk5nps$u3EQ^eI1aLA2Bw)sU}*pwHoWW3+16V=pSm;{3^Fk7I}U|H z&Tujg01yx$As~oAL;z8K*^p%y%H1loxP~Ks0@u{k z6lyGpAfCXIBtb5d0RY`S-PR8mzBfD`@9}+m4|b}mnhJ-*7N$iBTvb(7fE~vH5kZzE zxMUX|Jbd6pBA=O(Bwg6nvi;MBhK3;knd#uHu%al1u@C|t)eAk9LTl@3BOV{@kuE^EJD#UMz{kifzpjVdVcK+`YLTID6N3@xlY0c&OO%pF)wFLPI*ha4wkB`t7X6;ggCCltyU|N^uU4CD1seb zpY5lor-y_2%L*;&@r3|JU`b3&%&|8Qmt%;DeHWx85kJA>rZ7W>z_g~{^n#QukBwAZr zY3hvS=Y|)tzH?@t%5biivuE%W%^xLmsbQuS!kha{Vq2)U~n863@#e zMby;P=mQ-EW*0SlICRhUjbv_KkD`q6Ny`;hrKVA_BL%a`$ohaP|I#&1NG$E|xog-sb){3x}TEs9+uX`tD2)?S6)~i$CyV zPd^xp%m#xYugbE_%7<4YNz#IFLnQR*YgYCb=W+VGcDnio$+OvTxo#2gPXpld`4qq3 zpDoKWD^@)M5Q4|W#f9JoJw?ip$1A_}EPJ+RbLz}R20dPiic4_0ZsB#`q@!QO7#!VD zUtfRMZnxhU85yza%4ht34jw!}Wo0FRBLr54JdPt}Pg7cuN$a`G40^oSo-3RO4UG0$ zrWAJ)umV`=bUFi0r?Xg=Wz4B*G&VNk^Z7!ZFzR>d>(ZE2HAaU9AAnB$-d3QvE(PX< zBm;)d&dwk(JTWn`x1pio>#?yhR8_qi2n6h*5n#odl&$AGyuEsZ?oCb5(XSF`m@H6}Q^x@% z%=bD_|D&jL@SqNz7wN5szO;;u4EO4FLSpRN>#Y?d;V}(^N>O60&RrUy{R52l-tl6f R>M8&L002ovPDHLkV1i=@C_MlG diff --git a/chat/img/users.png b/chat/img/users.png deleted file mode 100644 index bced28cfb3f5a4bf5226ac40b7eefe293987f0da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1277 zcmVgVq)SYK~ZDk zGY|Npi5R0XNQg#FBry^d5g`ywi%{AWO1ZYQ-R^~MclYesv*$aX4_zv>80x%CW+wS( z=0B7F2&UQg&4K3y>Mey>3LsED3bE&l!{$N$hl$&R9h(E6)Rw3%?M?nbonPtzH%uXq z4_UdZrugdf!?{;x3#?Cd+V0;{=V#aUCBD*Bpjt}?>WJ%QI0|h|dEPscmGhhK^9-K3 z2_fDFIQD+If7ZYX7T5Zu zZhJ%ceLf;pl>`HRO1vI&nh#s^>FXk@>2!xL9lDmvp57{C=T2j+>qW8cZotOvfl>;m z;9%wRBoa5V@_FongOrlFOt5gtqHtvOCbU2$wiy??EJUIwf8N)%wYRlxKd;@Ds?X`@pvP*p+?V z^vs~bRcg=gLwR?hmLQuLBN~eli^p*%Vl-e;v*9_i74 z6X=-1@uN?!d}uhdYX7h=WPEd3Ql&DqYEkCt8A^m8HSQpFAFgAA5D1~&+BniqBbD$f zSa}D>o1R(M2K@2ZhH`F<1jN9=1lO-gl&ydq%A@gWDJbP4a|wiIfbK!&61e#kNE;~~ zOF(e?@;W!H3EN|_es4M(rDerx@>ZJui#^yjw6wI4y?h2I6UEKNue54_=9}->k!dPqbz&s zX`JuBdlz`mNT)KNp6H}wX>+xzs&|Deg2lVmNO{woyPCq41rUO9ymXB^?ArS>bNn85 ze|eTg^#bKeT*u+!NS5Xz#m1>$I(M(y&}Q@y+A*2?S6`~_5K6fy>EHq%A36^}d+Ru* z0R!a}ICSa?u9Rr{9qQmSfb^e7x?Ts~0ZNLOVgTV6RxCWNWC4Ica)fMY6#zf|bq&+9 zxo|xVN)k*ZrfNm=8-N330JA9kT2Vtx088pLH&jp{$DFD%%)F#`c#@iMI6T>&EMSzC zVHo-hbyI+x_E{!wn#s?~fBfMk0AI0CaSt7yuu>lZIdPBg;sXLjuT2-OTl#gIw~CWm nVirG9oAw+33;5RLE};Ga6c7WIAH0-{00000NkvXXu0mjf<;zyj diff --git a/chat/index.php b/chat/index.php index a7e2fd0..d411a86 100644 --- a/chat/index.php +++ b/chat/index.php @@ -2,13 +2,14 @@ /* * @package AJAX_Chat * @author Sebastian Tschan + * @author Philip Nicolcev * @copyright (c) Sebastian Tschan * @license Modified MIT License * @link https://blueimp.net/ajax/ */ -// Suppress errors. -error_reporting(0); +// Suppress errors: +error_reporting(E_ALL); // Path to the chat directory: define('AJAX_CHAT_PATH', dirname($_SERVER['SCRIPT_FILENAME']).'/'); @@ -21,4 +22,3 @@ // Initialize the chat: $ajaxChat = new CustomAJAXChat(); -?> \ No newline at end of file diff --git a/chat/js/FABridge.js b/chat/js/FABridge.js index 5942457..e89180e 100644 --- a/chat/js/FABridge.js +++ b/chat/js/FABridge.js @@ -1,591 +1,591 @@ -/* -/* -Copyright 2006 Adobe Systems Incorporated - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), -to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, -and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - - -/* - * The Bridge class, responsible for navigating AS instances - */ -function FABridge(target,bridgeName) -{ - this.target = target; - this.remoteTypeCache = {}; - this.remoteInstanceCache = {}; - this.remoteFunctionCache = {}; - this.localFunctionCache = {}; - this.bridgeID = FABridge.nextBridgeID++; - this.name = bridgeName; - this.nextLocalFuncID = 0; - FABridge.instances[this.name] = this; - FABridge.idMap[this.bridgeID] = this; - - return this; -} - -// type codes for packed values -FABridge.TYPE_ASINSTANCE = 1; -FABridge.TYPE_ASFUNCTION = 2; - -FABridge.TYPE_JSFUNCTION = 3; -FABridge.TYPE_ANONYMOUS = 4; - -FABridge.initCallbacks = {} - -FABridge.argsToArray = function(args) -{ - var result = []; - for (var i = 0; i < args.length; i++) - { - result[i] = args[i]; - } - return result; -} - -function instanceFactory(objID) -{ - this.fb_instance_id = objID; - return this; -} - -function FABridge__invokeJSFunction(args) -{ - var funcID = args[0]; - var throughArgs = args.concat();//FABridge.argsToArray(arguments); - throughArgs.shift(); - - var bridge = FABridge.extractBridgeFromID(funcID); - return bridge.invokeLocalFunction(funcID, throughArgs); -} - -FABridge.addInitializationCallback = function(bridgeName, callback) -{ - var inst = FABridge.instances[bridgeName]; - if (inst != undefined) - { - callback.call(inst); - return; - } - - var callbackList = FABridge.initCallbacks[bridgeName]; - if(callbackList == null) - { - FABridge.initCallbacks[bridgeName] = callbackList = []; - } - - callbackList.push(callback); -} - -function FABridge__bridgeInitialized(bridgeName) { - var objects = document.getElementsByTagName("object"); - var ol = objects.length; - var activeObjects = []; - if (ol > 0) { - for (var i = 0; i < ol; i++) { - if (typeof objects[i].SetVariable != "undefined") { - activeObjects[activeObjects.length] = objects[i]; - } - } - } - var embeds = document.getElementsByTagName("embed"); - var el = embeds.length; - var activeEmbeds = []; - if (el > 0) { - for (var j = 0; j < el; j++) { - if (typeof embeds[j].SetVariable != "undefined") { - activeEmbeds[activeEmbeds.length] = embeds[j]; - } - } - } - var aol = activeObjects.length; - var ael = activeEmbeds.length; - var searchStr = "bridgeName="+ bridgeName; - if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) { - FABridge.attachBridge(activeObjects[0], bridgeName); - } - else if (ael == 1 && !aol) { - FABridge.attachBridge(activeEmbeds[0], bridgeName); - } - else { - var flash_found = false; - if (aol > 1) { - for (var k = 0; k < aol; k++) { - var params = activeObjects[k].childNodes; - for (var l = 0; l < params.length; l++) { - var param = params[l]; - if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) { - FABridge.attachBridge(activeObjects[k], bridgeName); - flash_found = true; - break; - } - } - if (flash_found) { - break; - } - } - } - if (!flash_found && ael > 1) { - for (var m = 0; m < ael; m++) { - var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue; - if (flashVars.indexOf(searchStr) >= 0) { - FABridge.attachBridge(activeEmbeds[m], bridgeName); - break; - } - } - } - } - return true; -} - -// used to track multiple bridge instances, since callbacks from AS are global across the page. - -FABridge.nextBridgeID = 0; -FABridge.instances = {}; -FABridge.idMap = {}; -FABridge.refCount = 0; - -FABridge.extractBridgeFromID = function(id) -{ - var bridgeID = (id >> 16); - return FABridge.idMap[bridgeID]; -} - -FABridge.attachBridge = function(instance, bridgeName) -{ - var newBridgeInstance = new FABridge(instance, bridgeName); - - FABridge[bridgeName] = newBridgeInstance; - -/* FABridge[bridgeName] = function() { - return newBridgeInstance.root(); - } -*/ - var callbacks = FABridge.initCallbacks[bridgeName]; - if (callbacks == null) - { - return; - } - for (var i = 0; i < callbacks.length; i++) - { - callbacks[i].call(newBridgeInstance); - } - delete FABridge.initCallbacks[bridgeName] -} - -// some methods can't be proxied. You can use the explicit get,set, and call methods if necessary. - -FABridge.blockedMethods = -{ - toString: true, - get: true, - set: true, - call: true -}; - -FABridge.prototype = -{ - - -// bootstrapping - - root: function() - { - return this.deserialize(this.target.getRoot()); - }, -//clears all of the AS objects in the cache maps - releaseASObjects: function() - { - return this.target.releaseASObjects(); - }, -//clears a specific object in AS from the type maps - releaseNamedASObject: function(value) - { - if(typeof(value) != "object") - { - return false; - } - else - { - var ret = this.target.releaseNamedASObject(value.fb_instance_id); - return ret; - } - }, -//create a new AS Object - create: function(className) - { - return this.deserialize(this.target.create(className)); - }, - - - // utilities - - makeID: function(token) - { - return (this.bridgeID << 16) + token; - }, - - - // low level access to the flash object - -//get a named property from an AS object - getPropertyFromAS: function(objRef, propName) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.getPropFromAS(objRef, propName); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, -//set a named property on an AS object - setPropertyInAS: function(objRef,propName, value) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.setPropInAS(objRef,propName, this.serialize(value)); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, - -//call an AS function - callASFunction: function(funcID, args) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - retVal = this.target.invokeASFunction(funcID, this.serialize(args)); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, -//call a method on an AS object - callASMethod: function(objID, funcName, args) - { - if (FABridge.refCount > 0) - { - throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); - } - else - { - FABridge.refCount++; - args = this.serialize(args); - retVal = this.target.invokeASMethod(objID, funcName, args); - retVal = this.handleError(retVal); - FABridge.refCount--; - return retVal; - } - }, - - // responders to remote calls from flash - - //callback from flash that executes a local JS function - //used mostly when setting js functions as callbacks on events - invokeLocalFunction: function(funcID, args) - { - var result; - var func = this.localFunctionCache[funcID]; - - if(func != undefined) - { - result = this.serialize(func.apply(null, this.deserialize(args))); - } - - return result; - }, - - // Object Types and Proxies - - // accepts an object reference, returns a type object matching the obj reference. - getTypeFromName: function(objTypeName) - { - return this.remoteTypeCache[objTypeName]; - }, - //create an AS proxy for the given object ID and type - createProxy: function(objID, typeName) - { - var objType = this.getTypeFromName(typeName); - instanceFactory.prototype = objType; - var instance = new instanceFactory(objID); - this.remoteInstanceCache[objID] = instance; - return instance; - }, - //return the proxy associated with the given object ID - getProxy: function(objID) - { - return this.remoteInstanceCache[objID]; - }, - - // accepts a type structure, returns a constructed type - addTypeDataToCache: function(typeData) - { - newType = new ASProxy(this, typeData.name); - var accessors = typeData.accessors; - for (var i = 0; i < accessors.length; i++) - { - this.addPropertyToType(newType, accessors[i]); - } - - var methods = typeData.methods; - for (var i = 0; i < methods.length; i++) - { - if (FABridge.blockedMethods[methods[i]] == undefined) - { - this.addMethodToType(newType, methods[i]); - } - } - - - this.remoteTypeCache[newType.typeName] = newType; - return newType; - }, - - //add a property to a typename; used to define the properties that can be called on an AS proxied object - addPropertyToType: function(ty, propName) - { - var c = propName.charAt(0); - var setterName; - var getterName; - if(c >= "a" && c <= "z") - { - getterName = "get" + c.toUpperCase() + propName.substr(1); - setterName = "set" + c.toUpperCase() + propName.substr(1); - } - else - { - getterName = "get" + propName; - setterName = "set" + propName; - } - ty[setterName] = function(val) - { - this.bridge.setPropertyInAS(this.fb_instance_id, propName, val); - } - ty[getterName] = function() - { - return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); - } - }, - - //add a method to a typename; used to define the methods that can be callefd on an AS proxied object - addMethodToType: function(ty, methodName) - { - ty[methodName] = function() - { - return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments))); - } - }, - - // Function Proxies - - //returns the AS proxy for the specified function ID - getFunctionProxy: function(funcID) - { - var bridge = this; - if (this.remoteFunctionCache[funcID] == null) - { - this.remoteFunctionCache[funcID] = function() - { - bridge.callASFunction(funcID, FABridge.argsToArray(arguments)); - } - } - return this.remoteFunctionCache[funcID]; - }, - - //reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache - getFunctionID: function(func) - { - if (func.__bridge_id__ == undefined) - { - func.__bridge_id__ = this.makeID(this.nextLocalFuncID++); - this.localFunctionCache[func.__bridge_id__] = func; - } - return func.__bridge_id__; - }, - - // serialization / deserialization - - serialize: function(value) - { - var result = {}; - - var t = typeof(value); - //primitives are kept as such - if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined) - { - result = value; - } - else if (value instanceof Array) - { - //arrays are serializesd recursively - result = []; - for (var i = 0; i < value.length; i++) - { - result[i] = this.serialize(value[i]); - } - } - else if (t == "function") - { - //js functions are assigned an ID and stored in the local cache - result.type = FABridge.TYPE_JSFUNCTION; - result.value = this.getFunctionID(value); - } - else if (value instanceof ASProxy) - { - result.type = FABridge.TYPE_ASINSTANCE; - result.value = value.fb_instance_id; - } - else - { - result.type = FABridge.TYPE_ANONYMOUS; - result.value = value; - } - - return result; - }, - - //on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors - // the unpacking is done by returning the value on each pachet for objects/arrays - deserialize: function(packedValue) - { - - var result; - - var t = typeof(packedValue); - if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined) - { - result = this.handleError(packedValue); - } - else if (packedValue instanceof Array) - { - result = []; - for (var i = 0; i < packedValue.length; i++) - { - result[i] = this.deserialize(packedValue[i]); - } - } - else if (t == "object") - { - for(var i = 0; i < packedValue.newTypes.length; i++) - { - this.addTypeDataToCache(packedValue.newTypes[i]); - } - for (var aRefID in packedValue.newRefs) - { - this.createProxy(aRefID, packedValue.newRefs[aRefID]); - } - if (packedValue.type == FABridge.TYPE_PRIMITIVE) - { - result = packedValue.value; - } - else if (packedValue.type == FABridge.TYPE_ASFUNCTION) - { - result = this.getFunctionProxy(packedValue.value); - } - else if (packedValue.type == FABridge.TYPE_ASINSTANCE) - { - result = this.getProxy(packedValue.value); - } - else if (packedValue.type == FABridge.TYPE_ANONYMOUS) - { - result = packedValue.value; - } - } - return result; - }, - //increases the reference count for the given object - addRef: function(obj) - { - this.target.incRef(obj.fb_instance_id); - }, - //decrease the reference count for the given object and release it if needed - release:function(obj) - { - this.target.releaseRef(obj.fb_instance_id); - }, - - // check the given value for the components of the hard-coded error code : __FLASHERROR - // used to marshall NPE's into flash - - handleError: function(value) - { - if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0) - { - var myErrorMessage = value.split("||"); - if(FABridge.refCount > 0 ) - { - FABridge.refCount--; - } - throw new Error(myErrorMessage[1]); - return value; - } - else - { - return value; - } - } -}; - -// The root ASProxy class that facades a flash object - -ASProxy = function(bridge, typeName) -{ - this.bridge = bridge; - this.typeName = typeName; - return this; -}; -//methods available on each ASProxy object -ASProxy.prototype = -{ - get: function(propName) - { - return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); - }, - - set: function(propName, value) - { - this.bridge.setPropertyInAS(this.fb_instance_id, propName, value); - }, - - call: function(funcName, args) - { - this.bridge.callASMethod(this.fb_instance_id, funcName, args); - }, - - addRef: function() { - this.bridge.addRef(this); - }, - - release: function() { - this.bridge.release(this); - } -}; +/* +/* +Copyright 2006 Adobe Systems Incorporated + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + + +/* + * The Bridge class, responsible for navigating AS instances + */ +function FABridge(target,bridgeName) +{ + this.target = target; + this.remoteTypeCache = {}; + this.remoteInstanceCache = {}; + this.remoteFunctionCache = {}; + this.localFunctionCache = {}; + this.bridgeID = FABridge.nextBridgeID++; + this.name = bridgeName; + this.nextLocalFuncID = 0; + FABridge.instances[this.name] = this; + FABridge.idMap[this.bridgeID] = this; + + return this; +} + +// type codes for packed values +FABridge.TYPE_ASINSTANCE = 1; +FABridge.TYPE_ASFUNCTION = 2; + +FABridge.TYPE_JSFUNCTION = 3; +FABridge.TYPE_ANONYMOUS = 4; + +FABridge.initCallbacks = {} + +FABridge.argsToArray = function(args) +{ + var result = []; + for (var i = 0; i < args.length; i++) + { + result[i] = args[i]; + } + return result; +} + +function instanceFactory(objID) +{ + this.fb_instance_id = objID; + return this; +} + +function FABridge__invokeJSFunction(args) +{ + var funcID = args[0]; + var throughArgs = args.concat();//FABridge.argsToArray(arguments); + throughArgs.shift(); + + var bridge = FABridge.extractBridgeFromID(funcID); + return bridge.invokeLocalFunction(funcID, throughArgs); +} + +FABridge.addInitializationCallback = function(bridgeName, callback) +{ + var inst = FABridge.instances[bridgeName]; + if (inst != undefined) + { + callback.call(inst); + return; + } + + var callbackList = FABridge.initCallbacks[bridgeName]; + if(callbackList == null) + { + FABridge.initCallbacks[bridgeName] = callbackList = []; + } + + callbackList.push(callback); +} + +function FABridge__bridgeInitialized(bridgeName) { + var objects = document.getElementsByTagName("object"); + var ol = objects.length; + var activeObjects = []; + if (ol > 0) { + for (var i = 0; i < ol; i++) { + if (typeof objects[i].SetVariable != "undefined") { + activeObjects[activeObjects.length] = objects[i]; + } + } + } + var embeds = document.getElementsByTagName("embed"); + var el = embeds.length; + var activeEmbeds = []; + if (el > 0) { + for (var j = 0; j < el; j++) { + if (typeof embeds[j].SetVariable != "undefined") { + activeEmbeds[activeEmbeds.length] = embeds[j]; + } + } + } + var aol = activeObjects.length; + var ael = activeEmbeds.length; + var searchStr = "bridgeName="+ bridgeName; + if ((aol == 1 && !ael) || (aol == 1 && ael == 1)) { + FABridge.attachBridge(activeObjects[0], bridgeName); + } + else if (ael == 1 && !aol) { + FABridge.attachBridge(activeEmbeds[0], bridgeName); + } + else { + var flash_found = false; + if (aol > 1) { + for (var k = 0; k < aol; k++) { + var params = activeObjects[k].childNodes; + for (var l = 0; l < params.length; l++) { + var param = params[l]; + if (param.nodeType == 1 && param.tagName.toLowerCase() == "param" && param["name"].toLowerCase() == "flashvars" && param["value"].indexOf(searchStr) >= 0) { + FABridge.attachBridge(activeObjects[k], bridgeName); + flash_found = true; + break; + } + } + if (flash_found) { + break; + } + } + } + if (!flash_found && ael > 1) { + for (var m = 0; m < ael; m++) { + var flashVars = activeEmbeds[m].attributes.getNamedItem("flashVars").nodeValue; + if (flashVars.indexOf(searchStr) >= 0) { + FABridge.attachBridge(activeEmbeds[m], bridgeName); + break; + } + } + } + } + return true; +} + +// used to track multiple bridge instances, since callbacks from AS are global across the page. + +FABridge.nextBridgeID = 0; +FABridge.instances = {}; +FABridge.idMap = {}; +FABridge.refCount = 0; + +FABridge.extractBridgeFromID = function(id) +{ + var bridgeID = (id >> 16); + return FABridge.idMap[bridgeID]; +} + +FABridge.attachBridge = function(instance, bridgeName) +{ + var newBridgeInstance = new FABridge(instance, bridgeName); + + FABridge[bridgeName] = newBridgeInstance; + +/* FABridge[bridgeName] = function() { + return newBridgeInstance.root(); + } +*/ + var callbacks = FABridge.initCallbacks[bridgeName]; + if (callbacks == null) + { + return; + } + for (var i = 0; i < callbacks.length; i++) + { + callbacks[i].call(newBridgeInstance); + } + delete FABridge.initCallbacks[bridgeName] +} + +// some methods can't be proxied. You can use the explicit get,set, and call methods if necessary. + +FABridge.blockedMethods = +{ + toString: true, + get: true, + set: true, + call: true +}; + +FABridge.prototype = +{ + + +// bootstrapping + + root: function() + { + return this.deserialize(this.target.getRoot()); + }, +//clears all of the AS objects in the cache maps + releaseASObjects: function() + { + return this.target.releaseASObjects(); + }, +//clears a specific object in AS from the type maps + releaseNamedASObject: function(value) + { + if(typeof(value) != "object") + { + return false; + } + else + { + var ret = this.target.releaseNamedASObject(value.fb_instance_id); + return ret; + } + }, +//create a new AS Object + create: function(className) + { + return this.deserialize(this.target.create(className)); + }, + + + // utilities + + makeID: function(token) + { + return (this.bridgeID << 16) + token; + }, + + + // low level access to the flash object + +//get a named property from an AS object + getPropertyFromAS: function(objRef, propName) + { + if (FABridge.refCount > 0) + { + throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); + } + else + { + FABridge.refCount++; + retVal = this.target.getPropFromAS(objRef, propName); + retVal = this.handleError(retVal); + FABridge.refCount--; + return retVal; + } + }, +//set a named property on an AS object + setPropertyInAS: function(objRef,propName, value) + { + if (FABridge.refCount > 0) + { + throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); + } + else + { + FABridge.refCount++; + retVal = this.target.setPropInAS(objRef,propName, this.serialize(value)); + retVal = this.handleError(retVal); + FABridge.refCount--; + return retVal; + } + }, + +//call an AS function + callASFunction: function(funcID, args) + { + if (FABridge.refCount > 0) + { + throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); + } + else + { + FABridge.refCount++; + retVal = this.target.invokeASFunction(funcID, this.serialize(args)); + retVal = this.handleError(retVal); + FABridge.refCount--; + return retVal; + } + }, +//call a method on an AS object + callASMethod: function(objID, funcName, args) + { + if (FABridge.refCount > 0) + { + throw new Error("You are trying to call recursively into the Flash Player which is not allowed. In most cases the JavaScript setTimeout function, can be used as a workaround."); + } + else + { + FABridge.refCount++; + args = this.serialize(args); + retVal = this.target.invokeASMethod(objID, funcName, args); + retVal = this.handleError(retVal); + FABridge.refCount--; + return retVal; + } + }, + + // responders to remote calls from flash + + //callback from flash that executes a local JS function + //used mostly when setting js functions as callbacks on events + invokeLocalFunction: function(funcID, args) + { + var result; + var func = this.localFunctionCache[funcID]; + + if(func != undefined) + { + result = this.serialize(func.apply(null, this.deserialize(args))); + } + + return result; + }, + + // Object Types and Proxies + + // accepts an object reference, returns a type object matching the obj reference. + getTypeFromName: function(objTypeName) + { + return this.remoteTypeCache[objTypeName]; + }, + //create an AS proxy for the given object ID and type + createProxy: function(objID, typeName) + { + var objType = this.getTypeFromName(typeName); + instanceFactory.prototype = objType; + var instance = new instanceFactory(objID); + this.remoteInstanceCache[objID] = instance; + return instance; + }, + //return the proxy associated with the given object ID + getProxy: function(objID) + { + return this.remoteInstanceCache[objID]; + }, + + // accepts a type structure, returns a constructed type + addTypeDataToCache: function(typeData) + { + newType = new ASProxy(this, typeData.name); + var accessors = typeData.accessors; + for (var i = 0; i < accessors.length; i++) + { + this.addPropertyToType(newType, accessors[i]); + } + + var methods = typeData.methods; + for (var i = 0; i < methods.length; i++) + { + if (FABridge.blockedMethods[methods[i]] == undefined) + { + this.addMethodToType(newType, methods[i]); + } + } + + + this.remoteTypeCache[newType.typeName] = newType; + return newType; + }, + + //add a property to a typename; used to define the properties that can be called on an AS proxied object + addPropertyToType: function(ty, propName) + { + var c = propName.charAt(0); + var setterName; + var getterName; + if(c >= "a" && c <= "z") + { + getterName = "get" + c.toUpperCase() + propName.substr(1); + setterName = "set" + c.toUpperCase() + propName.substr(1); + } + else + { + getterName = "get" + propName; + setterName = "set" + propName; + } + ty[setterName] = function(val) + { + this.bridge.setPropertyInAS(this.fb_instance_id, propName, val); + } + ty[getterName] = function() + { + return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); + } + }, + + //add a method to a typename; used to define the methods that can be callefd on an AS proxied object + addMethodToType: function(ty, methodName) + { + ty[methodName] = function() + { + return this.bridge.deserialize(this.bridge.callASMethod(this.fb_instance_id, methodName, FABridge.argsToArray(arguments))); + } + }, + + // Function Proxies + + //returns the AS proxy for the specified function ID + getFunctionProxy: function(funcID) + { + var bridge = this; + if (this.remoteFunctionCache[funcID] == null) + { + this.remoteFunctionCache[funcID] = function() + { + bridge.callASFunction(funcID, FABridge.argsToArray(arguments)); + } + } + return this.remoteFunctionCache[funcID]; + }, + + //reutrns the ID of the given function; if it doesnt exist it is created and added to the local cache + getFunctionID: function(func) + { + if (func.__bridge_id__ == undefined) + { + func.__bridge_id__ = this.makeID(this.nextLocalFuncID++); + this.localFunctionCache[func.__bridge_id__] = func; + } + return func.__bridge_id__; + }, + + // serialization / deserialization + + serialize: function(value) + { + var result = {}; + + var t = typeof(value); + //primitives are kept as such + if (t == "number" || t == "string" || t == "boolean" || t == null || t == undefined) + { + result = value; + } + else if (value instanceof Array) + { + //arrays are serializesd recursively + result = []; + for (var i = 0; i < value.length; i++) + { + result[i] = this.serialize(value[i]); + } + } + else if (t == "function") + { + //js functions are assigned an ID and stored in the local cache + result.type = FABridge.TYPE_JSFUNCTION; + result.value = this.getFunctionID(value); + } + else if (value instanceof ASProxy) + { + result.type = FABridge.TYPE_ASINSTANCE; + result.value = value.fb_instance_id; + } + else + { + result.type = FABridge.TYPE_ANONYMOUS; + result.value = value; + } + + return result; + }, + + //on deserialization we always check the return for the specific error code that is used to marshall NPE's into JS errors + // the unpacking is done by returning the value on each pachet for objects/arrays + deserialize: function(packedValue) + { + + var result; + + var t = typeof(packedValue); + if (t == "number" || t == "string" || t == "boolean" || packedValue == null || packedValue == undefined) + { + result = this.handleError(packedValue); + } + else if (packedValue instanceof Array) + { + result = []; + for (var i = 0; i < packedValue.length; i++) + { + result[i] = this.deserialize(packedValue[i]); + } + } + else if (t == "object") + { + for(var i = 0; i < packedValue.newTypes.length; i++) + { + this.addTypeDataToCache(packedValue.newTypes[i]); + } + for (var aRefID in packedValue.newRefs) + { + this.createProxy(aRefID, packedValue.newRefs[aRefID]); + } + if (packedValue.type == FABridge.TYPE_PRIMITIVE) + { + result = packedValue.value; + } + else if (packedValue.type == FABridge.TYPE_ASFUNCTION) + { + result = this.getFunctionProxy(packedValue.value); + } + else if (packedValue.type == FABridge.TYPE_ASINSTANCE) + { + result = this.getProxy(packedValue.value); + } + else if (packedValue.type == FABridge.TYPE_ANONYMOUS) + { + result = packedValue.value; + } + } + return result; + }, + //increases the reference count for the given object + addRef: function(obj) + { + this.target.incRef(obj.fb_instance_id); + }, + //decrease the reference count for the given object and release it if needed + release:function(obj) + { + this.target.releaseRef(obj.fb_instance_id); + }, + + // check the given value for the components of the hard-coded error code : __FLASHERROR + // used to marshall NPE's into flash + + handleError: function(value) + { + if (typeof(value)=="string" && value.indexOf("__FLASHERROR")==0) + { + var myErrorMessage = value.split("||"); + if(FABridge.refCount > 0 ) + { + FABridge.refCount--; + } + throw new Error(myErrorMessage[1]); + return value; + } + else + { + return value; + } + } +}; + +// The root ASProxy class that facades a flash object + +ASProxy = function(bridge, typeName) +{ + this.bridge = bridge; + this.typeName = typeName; + return this; +}; +//methods available on each ASProxy object +ASProxy.prototype = +{ + get: function(propName) + { + return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName)); + }, + + set: function(propName, value) + { + this.bridge.setPropertyInAS(this.fb_instance_id, propName, value); + }, + + call: function(funcName, args) + { + this.bridge.callASMethod(this.fb_instance_id, funcName, args); + }, + + addRef: function() { + this.bridge.addRef(this); + }, + + release: function() { + this.bridge.release(this); + } +}; diff --git a/chat/js/chat.js b/chat/js/chat.js index 0fbd5f1..dfc1283 100644 --- a/chat/js/chat.js +++ b/chat/js/chat.js @@ -4,10 +4,10 @@ * @copyright (c) Sebastian Tschan * @license Modified MIT License * @link https://blueimp.net/ajax/ - * + * * The SELFHTML documentation has been used throughout this project: * http://selfhtml.org - * + * * Stylesheet and cookie methods have been inspired by Paul Sowden (A List Apart): * http://www.alistapart.com/stories/alternate/ */ @@ -85,21 +85,26 @@ var ajaxChat = { httpRequest: null, retryTimer: null, retryTimerDelay: null, - requestStatus: 'ok', + requestStatus: null, DOMbuffering: null, DOMbuffer: null, - DOMbufferRowClass: 'rowOdd', - - init: function(config, lang, initSettings, initStyle, initialize, initializeFunction, finalizeFunction) { + DOMbufferRowClass: null, + debug: null, + inUrlBBCode: null, + + init: function(config, lang, initSettings, initStyle, initialize, initializeFunction, finalizeFunction) { this.httpRequest = {}; this.usersList = []; this.userNamesList = []; this.userMenuCounter = 0; this.lastID = 0; this.localID = 0; - this.lang = lang; + this.lang = lang; + this.requestStatus = 'ok'; + this.DOMbufferRowClass = 'rowOdd'; + this.inUrlBBCode = false; this.initConfig(config); - this.initDirectories(); + this.initDirectories(); if(initSettings) { this.initSettings(); } @@ -112,7 +117,7 @@ var ajaxChat = { this.setLoadHandler(); } }, - + initConfig: function(config) { this.loginChannelID = config['loginChannelID']; this.loginChannelName = config['loginChannelName']; @@ -137,7 +142,7 @@ var ajaxChat = { this.chatBotName = config['chatBotName']; this.chatBotID = config['chatBotID']; this.allowUserMessageDelete = config['allowUserMessageDelete']; - this.inactiveTimeout = config['inactiveTimeout']; + this.inactiveTimeout = Math.max(config['inactiveTimeout'],2); this.privateChannelDiff = config['privateChannelDiff']; this.privateMessageDiff = config['privateMessageDiff']; this.showChannelMessages = config['showChannelMessages']; @@ -146,6 +151,7 @@ var ajaxChat = { this.socketServerHost = config['socketServerHost']; this.socketServerPort = config['socketServerPort']; this.socketServerChatID = config['socketServerChatID']; + this.debug = config['debug']; this.DOMbuffering = false; this.DOMbuffer = ""; this.retryTimerDelay = (this.inactiveTimeout*6000 - this.timerRate)/4 + this.timerRate; @@ -157,7 +163,7 @@ var ajaxChat = { this.dirs['sounds'] = this.baseURL+'sounds/'; this.dirs['flash'] = this.baseURL+'flash/'; }, - + initSettings: function() { var cookie = this.readCookie(this.sessionName + '_settings'), i, settingsArray, setting, key, value, number; @@ -209,21 +215,21 @@ var ajaxChat = { if(this.inArray(this.nonPersistentSettings, property)) { if(this.unusedSettings && this.unusedSettings[property]) { // Store the unusedSetting previously stored: - this.settings[property] = this.unusedSettings[property]; + this.settings[property] = this.unusedSettings[property]; } else { continue; } } settingsArray.push(property + '=' + this.encodeText(this.settings[property])); } - this.createCookie(this.sessionName + '_settings', settingsArray.join('&'), this.cookieExpiration); + this.createCookie(this.sessionName + '_settings', settingsArray.join('&'), this.cookieExpiration); } }, - + getSettings: function() { return this.settings; }, - + getSetting: function(key) { // Only return null if setting is null or undefined, not if it is false: for(var property in this.settings) { @@ -233,11 +239,11 @@ var ajaxChat = { } return null; }, - + setSetting: function(key, value) { this.settings[key] = value; }, - + initializeSettings: function() { if(this.settings['persistFontColor'] && this.settings['fontColor']) { // Set the inputField font color to the font color: @@ -246,17 +252,17 @@ var ajaxChat = { } } }, - - initialize: function() { + + initialize: function() { this.setUnloadHandler(); this.initializeDocumentNodes(); this.loadPageAttributes(); this.initEmoticons(); this.initColorCodes(); - this.initializeSettings(); + this.initializeSettings(); this.setSelectedStyle(); this.customInitialize(); - //preload the Alert icon (it can't display if there's no connection unless it's cached!) + // preload the Alert icon (it can't display if there's no connection unless it's cached!) this.setStatus('retrying'); if(typeof this.initializeFunction === 'function') { this.initializeFunction(); @@ -283,17 +289,17 @@ var ajaxChat = { } this.updateChat(params); }, - + setStartChatHandler: function() { if(this.dom['inputField']) { this.dom['inputField'].onfocus = function() { ajaxChat.startChat(); // Reset the onfocus event on first call: ajaxChat.dom['inputField'].onfocus = ''; - }; + }; } }, - + startChat: function() { this.chatStarted = true; if(this.dom['inputField'] && this.settings['autoFocus']) { @@ -306,24 +312,17 @@ var ajaxChat = { loadPageAttributes: function() { var htmlTag = document.getElementsByTagName('html')[0]; this.langCode = htmlTag.getAttribute('lang') ? htmlTag.getAttribute('lang') : 'en'; - this.baseDirection = htmlTag.getAttribute('dir') ? htmlTag.getAttribute('dir') : 'ltr'; + this.baseDirection = htmlTag.getAttribute('dir') ? htmlTag.getAttribute('dir') : 'ltr'; }, setLoadHandler: function() { + var self = this; // Make sure initialize() is called on page load: - var onload = window.onload; - if(typeof onload !== 'function') { - window.onload = function() { - ajaxChat.initialize(); - }; - } else { - window.onload = function() { - onload(); - ajaxChat.initialize(); - }; - } + this.addEvent(window,'load', function() { + self.initialize(); + }); }, - + setUnloadHandler: function() { // Make sure finalize() is called on page unload: var onunload = window.onunload; @@ -360,7 +359,7 @@ var ajaxChat = { this.updateChatlistView(); } }, - + initializeDocumentNodes: function() { this.dom = {}; for(var key in this.domIDs) { @@ -390,7 +389,7 @@ var ajaxChat = { } this.DOMbuffer = ""; }, - + initColorCodes: function() { if(this.dom['colorCodesContainer']) { this.DOMbuffer = ""; @@ -425,7 +424,7 @@ var ajaxChat = { } this.updateChat(params); }, - + updateChat: function(paramString) { var requestUrl = this.ajaxURL + '&lastID=' @@ -435,7 +434,7 @@ var ajaxChat = { } this.makeRequest(requestUrl,'GET',null); }, - + loadFlashInterface: function() { if(this.dom['flashInterfaceContainer']) { this.updateDOM( @@ -457,7 +456,7 @@ var ajaxChat = { FABridge.addInitializationCallback('ajaxChat', this.flashInterfaceLoadCompleteHandler); } }, - + flashInterfaceLoadCompleteHandler: function() { ajaxChat.initializeFlashInterface(); }, @@ -484,13 +483,13 @@ var ajaxChat = { } this.socket.connect(this.socketServerHost, this.socketServerPort); } catch(e) { - //alert(e); + this.debugMessage('socketConnect', e); } } clearTimeout(this.socketReconnectTimer); this.socketReconnectTimer = null; }, - + socketConnectHandler: function(event) { ajaxChat.socketIsConnected = true; // setTimeout is needed to avoid calling the flash interface recursively: @@ -504,7 +503,7 @@ var ajaxChat = { ajaxChat.updateChat(null); } }, - + socketDataHandler: function(event) { ajaxChat.socketUpdate(event.getData()); }, @@ -534,11 +533,11 @@ var ajaxChat = { +'"/>' ); } catch(e) { - //alert(e); + this.debugMessage('socketRegister', e); } } }, - + loadXML: function(str) { if(!arguments.callee.parser) { try { @@ -553,7 +552,7 @@ var ajaxChat = { arguments.callee.XMLDOM = new ActiveXObject('Microsoft.XMLDOM'); } arguments.callee.XMLDOM.loadXML(str); - return arguments.callee.XMLDOM; + return arguments.callee.XMLDOM; }; } else { // Safari, Konqueror: @@ -575,7 +574,7 @@ var ajaxChat = { } return arguments.callee.parser.parseFromString(str, 'text/xml'); }, - + socketUpdate: function(data) { var xmlDoc = this.loadXML(data); if(xmlDoc) { @@ -603,15 +602,15 @@ var ajaxChat = { this.settings['audioVolume'] = volume; try { if(!this.soundTransform) { - this.soundTransform = FABridge.ajaxChat.create('flash.media.SoundTransform'); + this.soundTransform = FABridge.ajaxChat.create('flash.media.SoundTransform'); } this.soundTransform.setVolume(volume); } catch(e) { - //alert(e); + this.debugMessage('setAudioVolume', e); } } }, - + loadSounds: function() { try { this.setAudioVolume(this.settings['audioVolume']); @@ -626,10 +625,10 @@ var ajaxChat = { sound.load(urlRequest); } } catch(e) { - alert(e); + this.debugMessage('loadSounds', e); } }, - + soundLoadCompleteHandler: function(event) { var sound = event.getTarget(); for(var key in ajaxChat.soundFiles) { @@ -646,7 +645,7 @@ var ajaxChat = { setTimeout(function() { ajaxChat.addChatBotMessageToChatList('/error SoundIO'); }, 0); setTimeout(ajaxChat.updateChatlistView, 1); }, - + soundPlayCompleteHandler: function(event) { // soundChannel event 'soundComplete' }, @@ -660,17 +659,21 @@ var ajaxChat = { // sndTransform:SoundTransform (default = null) return this.sounds[soundID].play(0, 0, this.soundTransform); } catch(e) { - //alert(e); + this.debugMessage('playSound', e); } } return null; }, - + playSoundOnNewMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { + var messageParts; if(this.settings['audio'] && this.sounds && this.lastID && !this.channelSwitch) { + if(this.customSoundOnNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) === false) { + return; + } + messageParts = messageText.split(' ', 1); switch(userID) { case this.chatBotID: - var messageParts = messageText.split(' ', 1); switch(messageParts[0]) { case '/login': case '/channelEnter': @@ -689,10 +692,15 @@ var ajaxChat = { } break; case this.userID: - this.playSound(this.settings['soundSend']); + switch(messageParts[0]) { + case '/privmsgto': + this.playSound(this.settings['soundPrivate']); + break; + default: + this.playSound(this.settings['soundSend']); + } break; default: - var messageParts = messageText.split(' ', 1); switch(messageParts[0]) { case '/privmsg': this.playSound(this.settings['soundPrivate']); @@ -720,32 +728,20 @@ var ajaxChat = { setStatus: function(newStatus) { // status options are: ok, retrying, waiting - if (this.requestStatus !== 'retrying' || newStatus === 'ok') { + if (!(newStatus === 'waiting' && this.requestStatus === 'retrying')) { this.requestStatus = newStatus; } - - var statusIcon = document.getElementById('statusIconContainer'); - if (statusIcon) { - switch (this.requestStatus) { - case 'ok': - this.setClass(statusIcon, 'statusContainerOff'); - break; - case 'waiting': - this.setClass(statusIcon, 'statusContainerOn'); - break; - case 'retrying': - this.setClass(statusIcon, 'statusContainerAlert'); - break; - } + if(this.dom['statusIcon']) { + this.dom['statusIcon'].className = this.requestStatus; } }, - + forceNewRequest: function() { - ajaxChat.updateChat(null); + ajaxChat.updateChat(null); ajaxChat.setStatus('retrying'); }, - + getHttpRequest: function(identifier) { if(!this.httpRequest[identifier]) { if (window.XMLHttpRequest) { @@ -768,9 +764,10 @@ var ajaxChat = { }, makeRequest: function(url, method, data) { - var identifier; - this.setStatus('waiting'); - + var self = this, + identifier; + self.setStatus('waiting'); + try { if(data) { // Create up to 50 HTTPRequest objects: @@ -784,17 +781,17 @@ var ajaxChat = { identifier = 0; } //if the response takes longer than retryTimerDelay to give an OK status, abort the connection and start again. - this.retryTimer = setTimeout(ajaxChat.forceNewRequest, ajaxChat.retryTimerDelay); - - this.getHttpRequest(identifier).open(method, url, true); - this.getHttpRequest(identifier).onreadystatechange = function() { + self.retryTimer = setTimeout(ajaxChat.forceNewRequest, ajaxChat.retryTimerDelay); + + self.getHttpRequest(identifier).open(method, url, true); + self.getHttpRequest(identifier).onreadystatechange = function() { try { ajaxChat.handleResponse(identifier); } catch(e) { try { clearTimeout(ajaxChat.timer); } catch(e) { - //alert(e); + self.debugMessage('makeRequest::clearTimeout', e); } try { if(data) { @@ -803,30 +800,30 @@ var ajaxChat = { ajaxChat.updateChatlistView(); } } catch(e) { - //alert(e); + self.debugMessage('makeRequest::logRetry', e); } - try { + try { ajaxChat.timer = setTimeout(function() { ajaxChat.updateChat(null); }, ajaxChat.timerRate); } catch(e) { - //alert(e); + self.debugMessage('makeRequest::setTimeout', e); } } }; if(method === 'POST') { - this.getHttpRequest(identifier).setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + self.getHttpRequest(identifier).setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); } - this.getHttpRequest(identifier).send(data); + self.getHttpRequest(identifier).send(data); } catch(e) { clearTimeout(this.timer); if(data) { - this.addChatBotMessageToChatList('/error ConnectionTimeout'); + self.addChatBotMessageToChatList('/error ConnectionTimeout'); ajaxChat.setStatus('retrying'); - this.updateChatlistView(); + self.updateChatlistView(); } - this.timer = setTimeout(function() { ajaxChat.updateChat(null); }, this.timerRate); + self.timer = setTimeout(function() { ajaxChat.updateChat(null); }, self.timerRate); } }, - + handleResponse: function(identifier) { var xmlDoc; if (this.getHttpRequest(identifier).readyState === 4) { @@ -843,7 +840,7 @@ var ajaxChat = { } else { this.addChatBotMessageToChatList('/error ConnectionStatus '+this.getHttpRequest(identifier).status); this.setStatus('retrying'); - this.updateChatlistView(); + this.updateChatlistView(); return false; } } @@ -854,7 +851,7 @@ var ajaxChat = { this.handleXML(xmlDoc); return true; }, - + handleXML: function(xmlDoc) { this.handleInfoMessages(xmlDoc.getElementsByTagName('info')); this.handleOnlineUsers(xmlDoc.getElementsByTagName('user')); @@ -876,10 +873,10 @@ var ajaxChat = { this.socketReconnectTimer = setTimeout(ajaxChat.socketConnect, 60000); } } - this.timer = setTimeout(function() {ajaxChat.updateChat(null);}, timeout); + this.timer = setTimeout(function() {ajaxChat.updateChat(null);}, timeout); } }, - + handleInfoMessages: function(infoNodes) { var infoType, infoData; for(var i=0; i= 0 ) { isInline = true; @@ -1091,7 +1089,7 @@ var ajaxChat = { this.showHide(menuID); this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight; }, - + getUserNodeStringItems: function(encodedUserName, userID, isInline) { var menu; if(encodedUserName !== this.encodedUserName) { @@ -1183,19 +1181,19 @@ var ajaxChat = { menu += this.getCustomUserMenuItems(encodedUserName, userID); return menu; }, - + setOnlineListRowClasses: function() { if(this.dom['onlineList']) { - var node = this.dom['onlineList'].firstChild; + var node = this.dom['onlineList'].firstChild; var rowEven = false; while(node) { - this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd')); + node.className = (rowEven ? 'rowEven' : 'rowOdd'); node = node.nextSibling; rowEven = !rowEven; } } }, - + clearChatList: function() { while(this.dom['chatList'].hasChildNodes()) { this.dom['chatList'].removeChild(this.dom['chatList'].firstChild); @@ -1218,7 +1216,7 @@ var ajaxChat = { } return arguments.callee.encodedChatBotName; }, - + addChatBotMessageToChatList: function(messageText) { this.addMessageToChatList( new Date(), @@ -1230,17 +1228,17 @@ var ajaxChat = { null ); }, - + addMessageToChatList: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { // Prevent adding the same message twice: if(this.getMessageNode(messageID)) { return; - } + } if(!this.onNewMessage(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip)) { return; } this.DOMbufferRowClass = this.DOMbufferRowClass === 'rowEven' ? 'rowOdd' : 'rowEven'; - this.DOMbuffer = this.DOMbuffer + + this.DOMbuffer = this.DOMbuffer + this.getChatListMessageString( dateObject, userID, userName, userRole, messageID, messageText, channelID, ip ); @@ -1251,15 +1249,17 @@ var ajaxChat = { }, getChatListMessageString: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { - var rowClass = this.DOMbufferRowClass; - var userClass = this.getRoleClass(userRole); - var colon; + var rowClass = this.DOMbufferRowClass, + userClass = this.getRoleClass(userRole), + colon = ': '; if(messageText.indexOf('/action') === 0 || messageText.indexOf('/me') === 0 || messageText.indexOf('/privaction') === 0) { userClass += ' action'; colon = ' '; - } else { - colon = ': '; } + if (messageText.indexOf('/privmsg') === 0 || messageText.indexOf('/privmsgto') === 0 || messageText.indexOf('/privaction') === 0) { + rowClass += ' private'; + } + var dateTime = this.settings['dateFormat'] ? '' + this.formatDate(this.settings['dateFormat'], dateObject) + ' ' : ''; return '
this.settings['maxMessages']) { this.dom['chatList'].removeChild(this.dom['chatList'].firstChild); } } - + if(this.settings['autoScroll']) { var self = this; setTimeout(function() { self.scrollChatList(); }, 50); @@ -1416,7 +1419,7 @@ var ajaxChat = { scrollChatList: function() { this.dom['chatList'].scrollTop = this.dom['chatList'].scrollHeight; }, - + encodeText: function(text) { return encodeURIComponent(text); }, @@ -1472,7 +1475,7 @@ var ajaxChat = { this.encodeSpecialCharsCallback ); }, - + encodeSpecialCharsCallback: function(str) { switch(str) { case '&': @@ -1493,13 +1496,13 @@ var ajaxChat = { decodeSpecialChars: function(text) { var regExp = new RegExp('(&)|(<)|(>)|(')|(")', 'g'); - + return text.replace( regExp, this.decodeSpecialCharsCallback ); }, - + decodeSpecialCharsCallback: function(str) { switch(str) { case '&': @@ -1516,7 +1519,7 @@ var ajaxChat = { return str; } }, - + inArray: function(haystack, needle) { var i = haystack.length; while(i--) { @@ -1528,20 +1531,24 @@ var ajaxChat = { }, arraySearch: function(needle, haystack) { - var i = haystack.length; - while(i--) { - if(haystack[i] === needle) { - return i; + if (!Array.prototype.indexOf) { // IE<9 + var i = haystack.length; + while(i--) { + if(haystack[i] === needle) { + return i; + } } + return -1; + } else { + return haystack.indexOf(needle); } - return false; }, stripTags: function(str) { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('<\\/?[^>]+?>', 'g'); } - + return str.replace(arguments.callee.regExp, ''); }, @@ -1549,9 +1556,9 @@ var ajaxChat = { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('\\[\\/?[^\\]]+?\\]', 'g'); } - + return str.replace(arguments.callee.regExp, ''); - }, + }, escapeRegExp: function(text) { if (!arguments.callee.regExp) { @@ -1565,7 +1572,7 @@ var ajaxChat = { } return text.replace(arguments.callee.regExp, '\\$1'); }, - + addSlashes: function(text) { // Adding slashes in front of apostrophs and backslashes to ensure a valid JavaScript expression: return text.replace(/\\/g, '\\\\').replace(/\'/g, '\\\''); @@ -1577,8 +1584,8 @@ var ajaxChat = { }, formatDate: function(format, date) { - date = (date == null) ? new date() : date; - + date = (date === null) ? new date() : date; + return format .replace(/%Y/g, date.getFullYear()) .replace(/%m/g, this.addLeadingZero(date.getMonth()+1)) @@ -1587,7 +1594,7 @@ var ajaxChat = { .replace(/%i/g, this.addLeadingZero(date.getMinutes())) .replace(/%s/g, this.addLeadingZero(date.getSeconds())); }, - + addLeadingZero: function(number) { number = number.toString(); if(number.length < 2) { @@ -1598,7 +1605,7 @@ var ajaxChat = { getUserIDFromUserName: function(userName) { var index = this.arraySearch(userName, this.userNamesList); - if(index !== false) { + if(index !== -1) { return this.usersList[index]; } return null; @@ -1606,7 +1613,7 @@ var ajaxChat = { getUserNameFromUserID: function(userID) { var index = this.arraySearch(userID, this.usersList); - if(index !== false) { + if(index !== -1) { return this.userNamesList[index]; } return null; @@ -1624,14 +1631,41 @@ var ajaxChat = { return 'admin'; case 4: return 'chatBot'; + case 5: + return 'customUser'; default: return 'default'; } }, - - handleInputFieldKeyPress: function(event) { + + handleInputFieldKeyDown: function(event) { + var text, lastWord, i; + + // Enter key without shift should send messages if(event.keyCode === 13 && !event.shiftKey) { this.sendMessage(); + try { + event.preventDefault(); + } catch(e) { + event.returnValue = false; // IE<9 + } + return false; + } + // Tab should complete usernames + else if(event.keyCode === 9 && !event.shiftKey) { + text = this.dom['inputField'].value; + if(text) { + lastWord = text.match(/\w+/g).slice(-1)[0]; + + if (lastWord.length > 2) { + for (i = 0; i < this.userNamesList.length; i++) { + if(this.userNamesList[i].replace("(","").toLowerCase().indexOf(lastWord.toLowerCase()) === 0) { + this.dom['inputField'].value = text.replace(new RegExp(lastWord + '$'), this.userNamesList[i]); + break; + } + } + } + } try { event.preventDefault(); } catch(e) { @@ -1645,7 +1679,7 @@ var ajaxChat = { handleInputFieldKeyUp: function(event) { this.updateMessageLengthCounter(); }, - + updateMessageLengthCounter: function() { if(this.dom['messageLengthCounter']) { this.updateDOM( @@ -1656,7 +1690,7 @@ var ajaxChat = { ); } }, - + sendMessage: function(text) { text = text ? text : this.dom['inputField'].value; if(!text) { @@ -1668,14 +1702,14 @@ var ajaxChat = { var message = 'lastID=' + this.lastID + '&text=' - + this.encodeText(text); + + this.encodeText(text); this.makeRequest(this.ajaxURL,'POST',message); } this.dom['inputField'].value = ''; this.dom['inputField'].focus(); this.updateMessageLengthCounter(); }, - + parseInputMessage: function(text) { var textParts; if(text.charAt(0) === '/') { @@ -1702,7 +1736,7 @@ var ajaxChat = { } return text; }, - + assignFontColorToMessage: function(text) { return '[color='+this.settings['fontColor']+']'+text+'[/color]'; }, @@ -1730,7 +1764,7 @@ var ajaxChat = { } return text; }, - + parseIgnoreInputCommand: function(text, textParts) { var userName, ignoredUserNames = this.getIgnoredUserNames(), i; if(textParts.length > 1) { @@ -1778,12 +1812,12 @@ var ajaxChat = { } return this.ignoredUserNames; }, - + setIgnoredUserNames: function(ignoredUserNames) { this.ignoredUserNames = ignoredUserNames; this.setSetting('ignoredUserNames', ignoredUserNames.join(' ')); }, - + ignoreMessage: function(dateObject, userID, userName, userRole, messageID, messageText, channelID, ip) { var textParts; if(userID === this.chatBotID && messageText.charAt(0) === '/') { @@ -1795,7 +1829,7 @@ var ajaxChat = { case '/roll': userName = textParts[1]; break; - } + } } } if(this.inArray(this.getIgnoredUserNames(), userName)) { @@ -1807,8 +1841,8 @@ var ajaxChat = { deleteMessage: function(messageID) { var messageNode = this.getMessageNode(messageID), originalClass, nextSibling; if(messageNode) { - originalClass = this.getClass(messageNode); - this.setClass(messageNode, originalClass+' deleteSelected'); + originalClass = messageNode.className; + this.addClass(messageNode, 'deleteSelected'); if(confirm(this.lang['deleteMessageConfirm'])) { nextSibling = messageNode.nextSibling; try { @@ -1818,10 +1852,10 @@ var ajaxChat = { } this.updateChat('&delete='+messageID); } catch(e) { - this.setClass(messageNode, originalClass); + messageNode.className = originalClass; } } else { - this.setClass(messageNode, originalClass); + messageNode.className = originalClass; } } }, @@ -1833,35 +1867,45 @@ var ajaxChat = { } if(node) { previousNode = node.previousSibling; - rowEven = (previousNode && this.getClass(previousNode) === 'rowOdd') ? true : false; + rowEven = (previousNode && previousNode.className === 'rowOdd') ? true : false; while(node) { - this.setClass(node, (rowEven ? 'rowEven' : 'rowOdd')); + node.className = (rowEven ? 'rowEven' : 'rowOdd'); node = node.nextSibling; rowEven = !rowEven; } } }, - - getClass: function(node) { - if(typeof node.className !== 'undefined') { - return node.className; // IE + + addEvent: function(elem, type, eventHandle) { + if (!elem) + return; + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); } else { - return node.getAttribute('class'); + elem["on"+type]=eventHandle; } }, - - setClass: function(node, className) { - if(typeof node.className !== 'undefined') { - node.className = className; // IE - } else { - node.setAttribute('class', className); + + addClass: function(node, theClass) { + if (!this.hasClass(node, theClass)) { + node.className += ' ' + theClass; } }, + removeClass: function(node, theClass) { + node.className = node.className.replace( new RegExp('(?:^|\\s)' + theClass + '(?!\\S)', 'g') , '' ); + }, + + hasClass: function(node, theClass) { + return node.className.match(new RegExp('\\b' + theClass + '\\b')); + }, + scriptLinkEncode: function(text) { return this.encodeText(this.addSlashes(this.decodeSpecialChars(text))); }, - + scriptLinkDecode: function(text) { return this.encodeSpecialChars(this.removeSlashes(this.decodeText(text))); }, @@ -1878,9 +1922,9 @@ var ajaxChat = { default: arguments.callee.utf8Decode = false; return value; - } + } } else if(arguments.callee.utf8Decode) { - return this.utf8Decode(value); + return this.utf8Decode(value); } else { return value; } @@ -1893,7 +1937,7 @@ var ajaxChat = { insertMessageWrapper: function(text) { this.insertText(this.getScriptLinkValue(text), true); }, - + switchChannel: function(channel) { if(!this.chatStarted) { this.clearChatList(); @@ -1903,11 +1947,11 @@ var ajaxChat = { this.requestTeaserContent(); return; } - clearTimeout(this.timer); + clearTimeout(this.timer); var message = 'lastID=' + this.lastID + '&channelName=' - + this.encodeText(channel); + + this.encodeText(channel); this.makeRequest(this.ajaxURL,'POST',message); if(this.dom['inputField'] && this.settings['autoFocus']) { this.dom['inputField'].focus(); @@ -1919,7 +1963,7 @@ var ajaxChat = { var message = 'logout=true'; this.makeRequest(this.ajaxURL,'POST',message); }, - + handleLogout: function(url) { window.location.href = url; }, @@ -1934,10 +1978,10 @@ var ajaxChat = { updateButton: function(setting, buttonID) { var node = document.getElementById(buttonID); if(node) { - this.setClass(node, (this.getSetting(setting) ? 'button' : 'button off')); + node.className = (this.getSetting(setting) ? 'button' : 'button off'); } }, - + showHide: function(id, styleDisplay, displayInline) { var node = document.getElementById(id); if(node) { @@ -1945,16 +1989,16 @@ var ajaxChat = { node.style.display = styleDisplay; } else { if(node.style.display === 'none') { - node.style.display = (displayInline ? 'inline' : 'block'); + node.style.display = (displayInline ? 'inline' : 'block'); } else { node.style.display = 'none'; } - } + } } }, setPersistFontColor: function(bool) { - this.settings['persistFontColor'] = bool; + this.settings['persistFontColor'] = bool; if(!this.settings['persistFontColor']) { this.settings['fontColor'] = null; if(this.dom['inputField']) { @@ -1979,16 +2023,16 @@ var ajaxChat = { this.insert('[color=' + color + ']', '[/color]'); } }, - + insertText: function(text, clearInputField) { if(clearInputField) { this.dom['inputField'].value = ''; } this.insert(text, ''); }, - + insertBBCode: function(bbCode) { - switch(bbCode) { + switch(bbCode) { case 'url': var url = prompt(this.lang['urlDialog'], 'http://'); if(url) @@ -1997,7 +2041,7 @@ var ajaxChat = { this.dom['inputField'].focus(); break; default: - this.insert('[' + bbCode + ']', '[/' + bbCode + ']'); + this.insert('[' + bbCode + ']', '[/' + bbCode + ']'); } }, @@ -2014,7 +2058,7 @@ var ajaxChat = { if (insText.length === 0) { range.move('character', -endTag.length); } else { - range.moveStart('character', startTag.length + insText.length + endTag.length); + range.moveStart('character', startTag.length + insText.length + endTag.length); } range.select(); } @@ -2048,7 +2092,7 @@ var ajaxChat = { + this.dom['inputField'].value.substr(pos); } }, - + replaceText: function(text) { try{ text = this.replaceLineBreaks(text); @@ -2059,20 +2103,20 @@ var ajaxChat = { text = this.replaceHyperLinks(text); text = this.replaceEmoticons(text); } - text = this.breakLongWords(text); + text = this.breakLongWords(text); text = this.replaceCustomText(text); } catch(e){ - //alert(e); + this.debugMessage('replaceText', e); } return text; }, - + replaceCommands: function(text) { try { if(text.charAt(0) !== '/') { return text; } - var textParts = text.split(' '); + var textParts = text.split(' '); switch(textParts[0]) { case '/login': return this.replaceCommandLogin(textParts); @@ -2143,7 +2187,7 @@ var ajaxChat = { return this.replaceCustomCommands(text, textParts); } } catch(e) { - //alert(e); + this.debugMessage('replaceCommands', e); } return text; }, @@ -2151,7 +2195,7 @@ var ajaxChat = { replaceCommandLogin: function(textParts) { return '' + this.lang['login'].replace(/%s/, textParts[1]) - + ''; + + ''; }, replaceCommandLogout: function(textParts) { @@ -2160,21 +2204,21 @@ var ajaxChat = { type = textParts[2]; return '' + this.lang['logout' + type].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandChannelEnter: function(textParts) { return '' + this.lang['channelEnter'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandChannelLeave: function(textParts) { return '' + this.lang['channelLeave'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandPrivMsg: function(textParts) { var privMsgText = textParts.slice(1).join(' '); privMsgText = this.replaceBBCode(privMsgText); @@ -2185,7 +2229,7 @@ var ajaxChat = { + ' ' + privMsgText; }, - + replaceCommandPrivMsgTo: function(textParts) { var privMsgText = textParts.slice(2).join(' '); privMsgText = this.replaceBBCode(privMsgText); @@ -2196,7 +2240,7 @@ var ajaxChat = { + ' ' + privMsgText; }, - + replaceCommandPrivAction: function(textParts) { var privActionText = textParts.slice(1).join(' '); privActionText = this.replaceBBCode(privActionText); @@ -2208,7 +2252,7 @@ var ajaxChat = { + this.lang['privmsg'] + ' '; }, - + replaceCommandPrivActionTo: function(textParts) { var privActionText = textParts.slice(2).join(' '); privActionText = this.replaceBBCode(privActionText); @@ -2218,9 +2262,9 @@ var ajaxChat = { + privActionText + ' ' + this.lang['privmsgto'].replace(/%s/, textParts[1]) - + ' '; + + ' '; }, - + replaceCommandAction: function(textParts) { var actionText = textParts.slice(1).join(' '); actionText = this.replaceBBCode(actionText); @@ -2228,9 +2272,9 @@ var ajaxChat = { actionText = this.replaceEmoticons(actionText); return '' + actionText - + ''; + + ''; }, - + replaceCommandInvite: function(textParts) { var inviteText = this.lang['invite'] .replace(/%s/, textParts[1]) @@ -2246,99 +2290,99 @@ var ajaxChat = { ); return '' + inviteText - + ''; + + ''; }, - + replaceCommandInviteTo: function(textParts) { var inviteText = this.lang['inviteto'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + inviteText - + ''; + + ''; }, - + replaceCommandUninvite: function(textParts) { var uninviteText = this.lang['uninvite'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + uninviteText - + ''; + + ''; }, - + replaceCommandUninviteTo: function(textParts) { var uninviteText = this.lang['uninviteto'] .replace(/%s/, textParts[1]) .replace(/%s/, textParts[2]); return '' + uninviteText - + ''; + + ''; }, - + replaceCommandQueryOpen: function(textParts) { return '' + this.lang['queryOpen'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandQueryClose: function(textParts) { return '' + this.lang['queryClose'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreAdded: function(textParts) { return '' + this.lang['ignoreAdded'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreRemoved: function(textParts) { return '' + this.lang['ignoreRemoved'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandIgnoreList: function(textParts) { return '' + this.lang['ignoreList'] + ' ' + this.getInlineUserMenu(textParts.slice(1)) - + ''; + + ''; }, - + replaceCommandIgnoreListEmpty: function(textParts) { return '' + this.lang['ignoreListEmpty'] - + ''; + + ''; }, - + replaceCommandKick: function(textParts) { return '' + this.lang['logoutKicked'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandWho: function(textParts) { return '' + this.lang['who'] + ' ' + this.getInlineUserMenu(textParts.slice(1)) - + ''; + + ''; }, replaceCommandWhoChannel: function(textParts) { return '' + this.lang['whoChannel'].replace(/%s/, textParts[1]) + ' ' + this.getInlineUserMenu(textParts.slice(2)) - + ''; + + ''; }, - + replaceCommandWhoEmpty: function(textParts) { return '' + this.lang['whoEmpty'] - + ''; + + ''; }, - + replaceCommandList: function(textParts) { var channels = textParts.slice(1); var listChannels = []; @@ -2358,9 +2402,9 @@ var ajaxChat = { return '' + this.lang['list'] + ' ' + listChannels.join(', ') - + ''; + + ''; }, - + replaceCommandBans: function(textParts) { var users = textParts.slice(1); var listUsers = []; @@ -2378,26 +2422,26 @@ var ajaxChat = { return '' + this.lang['bans'] + ' ' + listUsers.join(', ') - + ''; + + ''; }, - + replaceCommandBansEmpty: function(textParts) { return '' + this.lang['bansEmpty'] - + ''; + + ''; }, - + replaceCommandUnban: function(textParts) { return '' + this.lang['unban'].replace(/%s/, textParts[1]) - + ''; + + ''; }, - + replaceCommandWhois: function(textParts) { return '' + this.lang['whois'].replace(/%s/, textParts[1]) + ' ' + textParts[2] - + ''; + + ''; }, replaceCommandWhereis: function(textParts) { @@ -2412,24 +2456,24 @@ var ajaxChat = { + textParts[2] + '' ) - + ''; + + ''; }, - + replaceCommandRoll: function(textParts) { var rollText = this.lang['roll'].replace(/%s/, textParts[1]); rollText = rollText.replace(/%s/, textParts[2]); rollText = rollText.replace(/%s/, textParts[3]); return '' + rollText - + ''; + + ''; }, - + replaceCommandNick: function(textParts) { return '' + this.lang['nick'].replace(/%s/, textParts[1]).replace(/%s/, textParts[2]) - + ''; + + ''; }, - + replaceCommandError: function(textParts) { var errorMessage = this.lang['error'+textParts[1]]; if(!errorMessage) { @@ -2439,7 +2483,7 @@ var ajaxChat = { } return '' + errorMessage - + ''; + + ''; }, getInlineUserMenu: function(users) { @@ -2472,7 +2516,7 @@ var ajaxChat = { var openTags, closeTags, regExpOpenTags = /<[^>\/]+?>/gm, regExpCloseTags = /<\/[^>]+?>/gm; - + openTags = str.match(regExpOpenTags); closeTags = str.match(regExpCloseTags); // Return true if the number of tags doesn't match: @@ -2483,32 +2527,32 @@ var ajaxChat = { } return false; }, - + breakLongWords: function(text) { var newText, charCounter, currentChar, withinTag, withinEntity, i; - + if(!this.settings['wordWrap']) return text; - + newText = ''; charCounter = 0; - + for(i=0; i): if(i>5 && text.substr(i-5,4) === '
0 && text.charAt(i-1) === '>') { withinTag = false; // Reset the charCounter after newline tags (
): if(i>4 && text.substr(i-5,4) === '
0 && text.charAt(i-1) === ';') { @@ -2516,7 +2560,7 @@ var ajaxChat = { // We only increase the charCounter once for the whole entiy: charCounter++; } - + if(!withinTag && !withinEntity) { // Reset the charCounter if we encounter a word boundary: if(currentChar === ' ' || currentChar === '\n' || currentChar === '\t') { @@ -2530,14 +2574,14 @@ var ajaxChat = { newText += '​'; charCounter = 0; } - } + } // Add the current char to the text: newText += currentChar; } - + return newText; }, - + replaceBBCode: function(text) { if(!this.settings['bbCode']) { // If BBCode is disabled, just strip the text from BBCode tags: @@ -2545,11 +2589,11 @@ var ajaxChat = { } // Remove the BBCode tags: return text.replace( - /\[(\w+)(?:=([^<>]*?))?\](.+?)\[\/\1\]/gm, + /\[(\w+)(?:=([^<>]*?))?\](.+?)\[\/\1\]/gm, this.replaceBBCodeCallback ); }, - + replaceBBCodeCallback: function(str, p1, p2, p3) { // Only replace predefined BBCode tags: if(!ajaxChat.inArray(ajaxChat.bbCodeTags, p1)) { @@ -2581,7 +2625,7 @@ var ajaxChat = { if(this.settings['bbCodeColors']) { // Only allow predefined color codes: if(!attribute || !this.inArray(ajaxChat.colorCodes, attribute)) - return content; + return content; return '' + this.replaceBBCode(content) @@ -2589,9 +2633,9 @@ var ajaxChat = { } return content; }, - + replaceBBCodeUrl: function(content, attribute) { - var url, regExpUrl; + var url, regExpUrl, link; if(attribute) url = attribute.replace(/\s/gm, this.encodeText(' ')); else @@ -2602,15 +2646,19 @@ var ajaxChat = { ); if(!url || !url.match(regExpUrl)) return content; - return '' + this.replaceBBCode(content) + ''; + this.inUrlBBCode = false; + return link; }, replaceBBCodeImage: function(url) { - var regExpUrl, maxWidth, maxHeight; + var regExpUrl, maxWidth, maxHeight, link; if(this.settings['bbCodeImages']) { regExpUrl = new RegExp( this.regExpMediaUrl, @@ -2621,16 +2669,21 @@ var ajaxChat = { url = this.stripTags(url.replace(/\s/gm, this.encodeText(' '))); maxWidth = this.dom['chatList'].offsetWidth-50; maxHeight = this.dom['chatList'].offsetHeight-50; - return '' - +''; + link = ''; + if(!this.inUrlBBCode) { + link = '' + + link + + ''; + } + return link; } return url; }, @@ -2653,13 +2706,13 @@ var ajaxChat = { + this.replaceBBCode(content.replace(/\t|(?: )/gm, '  ')) + ''; }, - + replaceBBCodeUnderline: function(content) { return '' + this.replaceBBCode(content) + ''; }, - + replaceHyperLinks: function(text) { var regExp; if(!this.settings['hyperLinks']) { @@ -2712,11 +2765,11 @@ var ajaxChat = { arguments.callee.regExp = new RegExp(regExpStr, 'gm'); } return text.replace( - arguments.callee.regExp, + arguments.callee.regExp, this.replaceEmoticonsCallback ); }, - + replaceEmoticonsCallback: function(str, p1, p2, p3) { if (!arguments.callee.regExp) { arguments.callee.regExp = new RegExp('(="[^"]*$)|(&[^;]*$)', ''); @@ -2724,9 +2777,9 @@ var ajaxChat = { // Avoid replacing emoticons in tag attributes or XHTML entities: if(p1.match(arguments.callee.regExp)) { return str; - } + } if(p2) { - var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes); + var index = ajaxChat.arraySearch(p2, ajaxChat.emoticonCodes); return ajaxChat.replaceEmoticons(p1) + '_config = &$config; - - // Initialize custom configuration settings: - $this->initCustomConfig(); - } - - function initRequestVars() { - $this->_requestVars = array(); - $this->_requestVars['ajax'] = isset($_REQUEST['ajax']) ? true : false; - $this->_requestVars['userID'] = isset($_REQUEST['userID']) ? (int)$_REQUEST['userID'] : null; - $this->_requestVars['userName'] = isset($_REQUEST['userName']) ? $_REQUEST['userName'] : null; - $this->_requestVars['channelID'] = isset($_REQUEST['channelID']) ? (int)$_REQUEST['channelID'] : null; - $this->_requestVars['channelName'] = isset($_REQUEST['channelName']) ? $_REQUEST['channelName'] : null; - $this->_requestVars['text'] = isset($_POST['text']) ? $_POST['text'] : null; - $this->_requestVars['lastID'] = isset($_REQUEST['lastID']) ? (int)$_REQUEST['lastID'] : 0; - $this->_requestVars['login'] = isset($_REQUEST['login']) ? true : false; - $this->_requestVars['logout'] = isset($_REQUEST['logout']) ? true : false; - $this->_requestVars['password'] = isset($_REQUEST['password']) ? $_REQUEST['password'] : null; - $this->_requestVars['view'] = isset($_REQUEST['view']) ? $_REQUEST['view'] : null; - $this->_requestVars['year'] = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : null; - $this->_requestVars['month'] = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : null; - $this->_requestVars['day'] = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : null; - $this->_requestVars['hour'] = isset($_REQUEST['hour']) ? (int)$_REQUEST['hour'] : null; - $this->_requestVars['search'] = isset($_REQUEST['search']) ? $_REQUEST['search'] : null; - $this->_requestVars['shoutbox'] = isset($_REQUEST['shoutbox']) ? true : false; - $this->_requestVars['getInfos'] = isset($_REQUEST['getInfos']) ? $_REQUEST['getInfos'] : null; - $this->_requestVars['lang'] = isset($_REQUEST['lang']) ? $_REQUEST['lang'] : null; - $this->_requestVars['delete'] = isset($_REQUEST['delete']) ? (int)$_REQUEST['delete'] : null; - - // Initialize custom request variables: - $this->initCustomRequestVars(); - - // Remove slashes which have been added to user input strings if magic_quotes_gpc is On: - if(get_magic_quotes_gpc()) { - // It is safe to remove the slashes as we escape user data ourself - array_walk( - $this->_requestVars, - create_function( - '&$value, $key', - 'if(is_string($value)) $value = stripslashes($value);' - ) - ); - } - } - - function initDataBaseConnection() { - // Create a new database object: - $this->db = new AJAXChatDataBase( - $this->_config['dbConnection'] - ); - // Use a new database connection if no existing is given: - if(!$this->_config['dbConnection']['link']) { - // Connect to the database server: - $this->db->connect($this->_config['dbConnection']); - if($this->db->error()) { - echo $this->db->getError(); - die(); - } - // Select the database: - $this->db->select($this->_config['dbConnection']['name']); - if($this->db->error()) { - echo $this->db->getError(); - die(); - } - } - // Unset the dbConnection array for safety purposes: - unset($this->_config['dbConnection']); - } - - function getDataBaseTable($table) { - return ($this->db->getName() ? '`'.$this->db->getName().'`.'.$this->getConfig('dbTableNames',$table) : $this->getConfig('dbTableNames',$table)); - } - - function initSession() { - // Start the PHP session (if not already started): - $this->startSession(); - - if($this->isLoggedIn()) { - // Logout if we receive a logout request, the chat has been closed or the userID could not be revalidated: - if($this->getRequestVar('logout') || !$this->isChatOpen() || !$this->revalidateUserID()) { - $this->logout(); - return; - } - // Logout if the Session IP is not the same when logged in and ipCheck is enabled: - if($this->getConfig('ipCheck') && ($this->getSessionIP() === null || $this->getSessionIP() != $_SERVER['REMOTE_ADDR'])) { - $this->logout('IP'); - return; - } - } else if( - // Login if auto-login enabled or a login, userName or shoutbox parameter is given: - $this->getConfig('forceAutoLogin') || - $this->getRequestVar('login') || - $this->getRequestVar('userName') || - $this->getRequestVar('shoutbox') - ) { - $this->login(); - } - - // Initialize the view: - $this->initView(); - - if($this->getView() == 'chat') { - $this->initChatViewSession(); - } else if($this->getView() == 'logs') { - $this->initLogsViewSession(); - } - - if(!$this->getRequestVar('ajax') && !headers_sent()) { - // Set style cookie: - $this->setStyle(); - // Set langCode cookie: - $this->setLangCodeCookie(); - } - - $this->initCustomSession(); - } - - function initLogsViewSession() { - if($this->getConfig('socketServerEnabled')) { - if(!$this->getSessionVar('logsViewSocketAuthenticated')) { - $this->updateLogsViewSocketAuthentication(); - $this->setSessionVar('logsViewSocketAuthenticated', true); - } - } - } - - function updateLogsViewSocketAuthentication() { - if($this->getUserRole() != AJAX_CHAT_ADMIN) { - $channels = array(); - foreach($this->getChannels() as $channel) { - if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { - continue; - } - array_push($channels, $channel); - } - array_push($channels, $this->getPrivateMessageID()); - array_push($channels, $this->getPrivateChannelID()); - } else { - // The channelID "ALL" authenticates for all channels: - $channels = array('ALL'); - } - $this->updateSocketAuthentication( - $this->getUserID(), - $this->getSocketRegistrationID(), - $channels - ); - } - - function initChatViewSession() { - // If channel is not null we are logged in to the chat view: - if($this->getChannel() !== null) { - // Check if the current user has been logged out due to inactivity: - if(!$this->isUserOnline()) { - $this->logout(); - return; - } - if($this->getRequestVar('ajax')) { - $this->initChannel(); - $this->updateOnlineStatus(); - $this->checkAndRemoveInactive(); - } - } else { - if($this->getRequestVar('ajax')) { - // Set channel, insert login messages and add to online list on first ajax request in chat view: - $this->chatViewLogin(); - } - } - } - - function isChatOpen() { - if($this->getUserRole() == AJAX_CHAT_ADMIN) - return true; - if($this->getConfig('chatClosed')) - return false; - $time = time(); - if($this->getConfig('timeZoneOffset') !== null) { - // Subtract the server timezone offset and add the config timezone offset: - $time -= date('Z', $time); - $time += $this->getConfig('timeZoneOffset'); - } - // Check the opening hours: - if($this->getConfig('openingHour') < $this->getConfig('closingHour')) - { - if(($this->getConfig('openingHour') > date('G', $time)) || ($this->getConfig('closingHour') <= date('G', $time))) - return false; - } - else - { - if(($this->getConfig('openingHour') > date('G', $time)) && ($this->getConfig('closingHour') <= date('G', $time))) - return false; - } - // Check the opening weekdays: - if(!in_array(date('w', $time), $this->getConfig('openingWeekDays'))) - return false; - return true; - } - - function handleRequest() { - if($this->getRequestVar('ajax')) { - if($this->isLoggedIn()) { - // Parse info requests (for current userName, etc.): - $this->parseInfoRequests(); - - // Parse command requests (e.g. message deletion): - $this->parseCommandRequests(); - - // Parse message requests: - $this->initMessageHandling(); - } - // Send chat messages and online user list in XML format: - $this->sendXMLMessages(); - } else { - // Display XHTML content for non-ajax requests: - $this->sendXHTMLContent(); - } - } - - function parseCommandRequests() { - if($this->getRequestVar('delete') !== null) { - $this->deleteMessage($this->getRequestVar('delete')); - } - } - - function parseInfoRequests() { - if($this->getRequestVar('getInfos')) { - $infoRequests = explode(',', $this->getRequestVar('getInfos')); - foreach($infoRequests as $infoRequest) { - $this->parseInfoRequest($infoRequest); - } - } - } - - function parseInfoRequest($infoRequest) { - switch($infoRequest) { - case 'userID': - $this->addInfoMessage($this->getUserID(), 'userID'); - break; - case 'userName': - $this->addInfoMessage($this->getUserName(), 'userName'); - break; - case 'userRole': - $this->addInfoMessage($this->getUserRole(), 'userRole'); - break; - case 'channelID': - $this->addInfoMessage($this->getChannel(), 'channelID'); - break; - case 'channelName': - $this->addInfoMessage($this->getChannelName(), 'channelName'); - break; - case 'socketRegistrationID': - $this->addInfoMessage($this->getSocketRegistrationID(), 'socketRegistrationID'); - break; - default: - $this->parseCustomInfoRequest($infoRequest); - } - } - - function sendXHTMLContent() { - $httpHeader = new AJAXChatHTTPHeader($this->getConfig('contentEncoding'), $this->getConfig('contentType')); - - $template = new AJAXChatTemplate($this, $this->getTemplateFileName(), $httpHeader->getContentType()); - - // Send HTTP header: - $httpHeader->send(); - - // Send parsed template content: - echo $template->getParsedContent(); - } - - function getTemplateFileName() { - switch($this->getView()) { - case 'chat': - return AJAX_CHAT_PATH.'lib/template/loggedIn.html'; - case 'logs': - return AJAX_CHAT_PATH.'lib/template/logs.html'; - default: - return AJAX_CHAT_PATH.'lib/template/loggedOut.html'; - } - } - - function initView() { - $this->_view = null; - // "chat" is the default view: - $view = ($this->getRequestVar('view') === null) ? 'chat' : $this->getRequestVar('view'); - if($this->hasAccessTo($view)) { - $this->_view = $view; - } - } - - function getView() { - return $this->_view; - } - - function hasAccessTo($view) { - switch($view) { - case 'chat': - case 'teaser': - if($this->isLoggedIn()) { - return true; - } - return false; - case 'logs': - if($this->isLoggedIn() && ($this->getUserRole() == AJAX_CHAT_ADMIN || - ($this->getConfig('logsUserAccess') && - ($this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == AJAX_CHAT_USER)) - )) { - return true; - } - return false; - default: - return false; - } - } - - function login() { - // Retrieve valid login user data (from request variables or session data): - $userData = $this->getValidLoginUserData(); - - if(!$userData) { - $this->addInfoMessage('errorInvalidUser'); - return false; - } - - // If the chat is closed, only the admin may login: - if(!$this->isChatOpen() && $userData['userRole'] != AJAX_CHAT_ADMIN) { - $this->addInfoMessage('errorChatClosed'); - return false; - } - - if(!$this->getConfig('allowGuestLogins') && $userData['userRole'] == AJAX_CHAT_GUEST) { - return false; - } - - // Check if userID or userName are already listed online: - if($this->isUserOnline($userData['userID']) || $this->isUserNameInUse($userData['userName'])) { - if($userData['userRole'] == AJAX_CHAT_USER || $userData['userRole'] == AJAX_CHAT_MODERATOR || $userData['userRole'] == AJAX_CHAT_ADMIN) { - // Set the registered user inactive and remove the inactive users so the user can be logged in again: - $this->setInactive($userData['userID'], $userData['userName']); - $this->removeInactive(); - } else { - $this->addInfoMessage('errorUserInUse'); - return false; - } - } - - // Check if user is banned: - if($userData['userRole'] != AJAX_CHAT_ADMIN && $this->isUserBanned($userData['userName'], $userData['userID'], $_SERVER['REMOTE_ADDR'])) { - $this->addInfoMessage('errorBanned'); - return false; - } - - // Check if the max number of users is logged in (not affecting moderators or admins): - if(!($userData['userRole'] == AJAX_CHAT_MODERATOR || $userData['userRole'] == AJAX_CHAT_ADMIN) && $this->isMaxUsersLoggedIn()) { - $this->addInfoMessage('errorMaxUsersLoggedIn'); - return false; - } - - // Use a new session id (if session has been started by the chat): - $this->regenerateSessionID(); - - // Log in: - $this->setUserID($userData['userID']); - $this->setUserName($userData['userName']); - $this->setLoginUserName($userData['userName']); - $this->setUserRole($userData['userRole']); - $this->setLoggedIn(true); - $this->setLoginTimeStamp(time()); - - // IP Security check variable: - $this->setSessionIP($_SERVER['REMOTE_ADDR']); - - // The client authenticates to the socket server using a socketRegistrationID: - if($this->getConfig('socketServerEnabled')) { - $this->setSocketRegistrationID( - md5(uniqid(rand(), true)) - ); - } - - // Add userID, userName and userRole to info messages: - $this->addInfoMessage($this->getUserID(), 'userID'); - $this->addInfoMessage($this->getUserName(), 'userName'); - $this->addInfoMessage($this->getUserRole(), 'userRole'); - - // Purge logs: - if($this->getConfig('logsPurgeLogs')) { - $this->purgeLogs(); - } - - return true; - } - - function chatViewLogin() { - $this->setChannel($this->getValidRequestChannelID()); - $this->addToOnlineList(); - - // Add channelID and channelName to info messages: - $this->addInfoMessage($this->getChannel(), 'channelID'); - $this->addInfoMessage($this->getChannelName(), 'channelName'); - - // Login message: - $text = '/login '.$this->getUserName(); - $this->insertChatBotMessage( - $this->getChannel(), - $text, - null, - 1 - ); - } - - function getValidRequestChannelID() { - $channelID = $this->getRequestVar('channelID'); - $channelName = $this->getRequestVar('channelName'); - // Check the given channelID, or get channelID from channelName: - if($channelID === null) { - if($channelName !== null) { - $channelID = $this->getChannelIDFromChannelName($channelName); - // channelName might need encoding conversion: - if($channelID === null) { - $channelID = $this->getChannelIDFromChannelName( - $this->trimChannelName($channelName, $this->getConfig('contentEncoding')) - ); - } - } - } - // Validate the resulting channelID: - if(!$this->validateChannel($channelID)) { - if($this->getChannel() !== null) { - return $this->getChannel(); - } - return $this->getConfig('defaultChannelID'); - } - return $channelID; - } - - function initChannel() { - $channelID = $this->getRequestVar('channelID'); - $channelName = $this->getRequestVar('channelName'); - if($channelID !== null) { - $this->switchChannel($this->getChannelNameFromChannelID($channelID)); - } else if($channelName !== null) { - if($this->getChannelIDFromChannelName($channelName) === null) { - // channelName might need encoding conversion: - $channelName = $this->trimChannelName($channelName, $this->getConfig('contentEncoding')); - } - $this->switchChannel($channelName); - } - } - - function logout($type=null) { - // Update the socket server authentication for the user: - if($this->getConfig('socketServerEnabled')) { - $this->updateSocketAuthentication($this->getUserID()); - } - if($this->isUserOnline()) { - $this->chatViewLogout($type); - } - $this->setLoggedIn(false); - $this->destroySession(); - - // Re-initialize the view: - $this->initView(); - } - - function chatViewLogout($type) { - $this->removeFromOnlineList(); - if($type !== null) { - $type = ' '.$type; - } - // Logout message - $text = '/logout '.$this->getUserName().$type; - $this->insertChatBotMessage( - $this->getChannel(), - $text, - null, - 1 - ); - } - - function switchChannel($channelName) { - $channelID = $this->getChannelIDFromChannelName($channelName); - - if($channelID !== null && $this->getChannel() == $channelID) { - // User is already in the given channel, return: - return; - } - - // Check if we have a valid channel: - if(!$this->validateChannel($channelID)) { - // Invalid channel: - $text = '/error InvalidChannelName '.$channelName; - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - $text - ); - return; - } - - $oldChannel = $this->getChannel(); - - $this->setChannel($channelID); - $this->updateOnlineList(); - - // Channel leave message - $text = '/channelLeave '.$this->getUserName(); - $this->insertChatBotMessage( - $oldChannel, - $text, - null, - 1 - ); - - // Channel enter message - $text = '/channelEnter '.$this->getUserName(); - $this->insertChatBotMessage( - $this->getChannel(), - $text, - null, - 1 - ); - - $this->addInfoMessage($channelName, 'channelSwitch'); - $this->addInfoMessage($channelID, 'channelID'); - $this->_requestVars['lastID'] = 0; - } - - function addToOnlineList() { - $sql = 'INSERT INTO '.$this->getDataBaseTable('online').'( - userID, - userName, - userRole, - channel, - dateTime, - ip - ) - VALUES ( - '.$this->db->makeSafe($this->getUserID()).', - '.$this->db->makeSafe($this->getUserName()).', - '.$this->db->makeSafe($this->getUserRole()).', - '.$this->db->makeSafe($this->getChannel()).', - NOW(), - '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' - );'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $this->resetOnlineUsersData(); - } - - function removeFromOnlineList() { - $sql = 'DELETE FROM - '.$this->getDataBaseTable('online').' - WHERE - userID = '.$this->db->makeSafe($this->getUserID()).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $this->removeUserFromOnlineUsersData(); - } - - function updateOnlineList() { - $sql = 'UPDATE - '.$this->getDataBaseTable('online').' - SET - userName = '.$this->db->makeSafe($this->getUserName()).', - channel = '.$this->db->makeSafe($this->getChannel()).', - dateTime = NOW(), - ip = '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' - WHERE - userID = '.$this->db->makeSafe($this->getUserID()).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $this->resetOnlineUsersData(); - } - - function initMessageHandling() { - // Don't handle messages if we are not in chat view: - if($this->getView() != 'chat') { - return; - } - - // Check if we have been uninvited from a private or restricted channel: - if(!$this->validateChannel($this->getChannel())) { - // Switch to the default channel: - $this->switchChannel($this->getChannelNameFromChannelID($this->getConfig('defaultChannelID'))); - return; - } - - if($this->getRequestVar('text') !== null) { - $this->insertMessage($this->getRequestVar('text')); - } - } - - function insertParsedMessage($text) { - - // If a queryUserName is set, sent all messages as private messages to this userName: - if($this->getQueryUserName() !== null && strpos($text, '/') !== 0) { - $text = '/msg '.$this->getQueryUserName().' '.$text; - } - - // Parse IRC-style commands: - if(strpos($text, '/') === 0) { - $textParts = explode(' ', $text); - - switch($textParts[0]) { - - // Channel switch: - case '/join': - $this->insertParsedMessageJoin($textParts); - break; - - // Logout: - case '/quit': - $this->logout(); - break; - - // Private message: - case '/msg': - case '/describe': - $this->insertParsedMessagePrivMsg($textParts); - break; - - // Invitation: - case '/invite': - $this->insertParsedMessageInvite($textParts); - break; - - // Uninvitation: - case '/uninvite': - $this->insertParsedMessageUninvite($textParts); - break; - - // Private messaging: - case '/query': - $this->insertParsedMessageQuery($textParts); - break; - - // Kicking offending users from the chat: - case '/kick': - $this->insertParsedMessageKick($textParts); - break; - - // Listing banned users: - case '/bans': - $this->insertParsedMessageBans($textParts); - break; - - // Unban user (remove from ban list): - case '/unban': - $this->insertParsedMessageUnban($textParts); - break; - - // Describing actions: - case '/me': - case '/action': - $this->insertParsedMessageAction($textParts); - break; - - - // Listing online Users: - case '/who': - $this->insertParsedMessageWho($textParts); - break; - - // Listing available channels: - case '/list': - $this->insertParsedMessageList($textParts); - break; - - // Retrieving the channel of a User: - case '/whereis': - $this->insertParsedMessageWhereis($textParts); - break; - - // Listing information about a User: - case '/whois': - $this->insertParsedMessageWhois($textParts); - break; - - // Rolling dice: - case '/roll': - $this->insertParsedMessageRoll($textParts); - break; - - // Switching userName: - case '/nick': - $this->insertParsedMessageNick($textParts); - break; - - // Custom or unknown command: - default: - if(!$this->parseCustomCommands($text, $textParts)) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UnknownCommand '.$textParts[0] - ); - } - } - - } else { - // No command found, just insert the plain message: - $this->insertCustomMessage( - $this->getUserID(), - $this->getUserName(), - $this->getUserRole(), - $this->getChannel(), - $text - ); - } - } - - function insertParsedMessageJoin($textParts) { - if(count($textParts) == 1) { - // join with no arguments is the own private channel, if allowed: - if($this->isAllowedToCreatePrivateChannel()) { - // Private channels are identified by square brackets: - $this->switchChannel($this->getChannelNameFromChannelID($this->getPrivateChannelID())); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingChannelName' - ); - } - } else { - $this->switchChannel($textParts[1]); - } - } - - function insertParsedMessagePrivMsg($textParts) { - if($this->isAllowedToSendPrivateMessage()) { - if(count($textParts) < 3) { - if(count($textParts) == 2) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingText' - ); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } - } else { - // Get UserID from UserName: - $toUserID = $this->getIDFromName($textParts[1]); - if($toUserID === null) { - if($this->getQueryUserName() !== null) { - // Close the current query: - $this->insertMessage('/query'); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } - } else { - // Insert /privaction command if /describe is used: - $command = ($textParts[0] == '/describe') ? '/privaction' : '/privmsg'; - // Copy of private message to current User: - $this->insertCustomMessage( - $this->getUserID(), - $this->getUserName(), - $this->getUserRole(), - $this->getPrivateMessageID(), - $command.'to '.$textParts[1].' '.implode(' ', array_slice($textParts, 2)) - ); - // Private message to requested User: - $this->insertCustomMessage( - $this->getUserID(), - $this->getUserName(), - $this->getUserRole(), - $this->getPrivateMessageID($toUserID), - $command.' '.implode(' ', array_slice($textParts, 2)) - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error PrivateMessageNotAllowed' - ); - } - } - - function insertParsedMessageInvite($textParts) { - if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - $toUserID = $this->getIDFromName($textParts[1]); - if($toUserID === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // Add the invitation to the database: - $this->addInvitation($toUserID); - $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); - // Copy of invitation to current User: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/inviteto '.$textParts[1].' '.$invitationChannelName - ); - // Invitation to requested User: - $this->insertChatBotMessage( - $this->getPrivateMessageID($toUserID), - '/invite '.$this->getUserName().' '.$invitationChannelName - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error InviteNotAllowed' - ); - } - } - - function insertParsedMessageUninvite($textParts) { - if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - $toUserID = $this->getIDFromName($textParts[1]); - if($toUserID === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // Remove the invitation from the database: - $this->removeInvitation($toUserID); - $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); - // Copy of uninvitation to current User: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/uninviteto '.$textParts[1].' '.$invitationChannelName - ); - // Uninvitation to requested User: - $this->insertChatBotMessage( - $this->getPrivateMessageID($toUserID), - '/uninvite '.$this->getUserName().' '.$invitationChannelName - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UninviteNotAllowed' - ); - } - } - - function insertParsedMessageQuery($textParts) { - if($this->isAllowedToSendPrivateMessage()) { - if(count($textParts) == 1) { - if($this->getQueryUserName() !== null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/queryClose '.$this->getQueryUserName() - ); - // Close the current query: - $this->setQueryUserName(null); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error NoOpenQuery' - ); - } - } else { - if($this->getIDFromName($textParts[1]) === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - if($this->getQueryUserName() !== null) { - // Close the current query: - $this->insertMessage('/query'); - } - // Open a query to the requested user: - $this->setQueryUserName($textParts[1]); - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/queryOpen '.$textParts[1] - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error PrivateMessageNotAllowed' - ); - } - } - - function insertParsedMessageKick($textParts) { - // Only moderators/admins may kick users: - if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - // Get UserID from UserName: - $kickUserID = $this->getIDFromName($textParts[1]); - if($kickUserID === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // Check the role of the user to kick: - $kickUserRole = $this->getRoleFromID($kickUserID); - if($kickUserRole == AJAX_CHAT_ADMIN || ($kickUserRole == AJAX_CHAT_MODERATOR && $this->getUserRole() != AJAX_CHAT_ADMIN)) { - // Admins and moderators may not be kicked: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error KickNotAllowed '.$textParts[1] - ); - } else { - // Kick user and insert message: - $channel = $this->getChannelFromID($kickUserID); - $banMinutes = (count($textParts) > 2) ? $textParts[2] : null; - $this->kickUser($textParts[1], $banMinutes, $kickUserID); - // If no channel found, user logged out before he could be kicked - if($channel !== null) { - $this->insertChatBotMessage( - $channel, - '/kick '.$textParts[1], - null, - 1 - ); - // Send a copy of the message to the current user, if not in the channel: - if($channel != $this->getChannel()) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/kick '.$textParts[1], - null, - 1 - ); - } - } - } - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error CommandNotAllowed '.$textParts[0] - ); - } - } - - function insertParsedMessageBans($textParts) { - // Only moderators/admins may see the list of banned users: - if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { - $this->removeExpiredBans(); - $bannedUsers = $this->getBannedUsers(); - if(count($bannedUsers) > 0) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/bans '.implode(' ', $bannedUsers) - ); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/bansEmpty -' - ); - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error CommandNotAllowed '.$textParts[0] - ); - } - } - - function insertParsedMessageUnban($textParts) { - // Only moderators/admins may unban users: - if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { - $this->removeExpiredBans(); - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - if(!in_array($textParts[1], $this->getBannedUsers())) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // Unban user and insert message: - $this->unbanUser($textParts[1]); - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/unban '.$textParts[1] - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error CommandNotAllowed '.$textParts[0] - ); - } - } - - function insertParsedMessageAction($textParts) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingText' - ); - } else { - if($this->getQueryUserName() !== null) { - // If we are in query mode, sent the action to the query user: - $this->insertMessage('/describe '.$this->getQueryUserName().' '.implode(' ', array_slice($textParts, 1))); - } else { - $this->insertCustomMessage( - $this->getUserID(), - $this->getUserName(), - $this->getUserRole(), - $this->getChannel(), - implode(' ', $textParts) - ); - } - } - } - - function insertParsedMessageWho($textParts) { - if(count($textParts) == 1) { - if($this->isAllowedToListHiddenUsers()) { - // List online users from any channel: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/who '.implode(' ', $this->getOnlineUsers()) - ); - } else { - // Get online users for all accessible channels: - $channels = $this->getChannels(); - // Add the own private channel if allowed: - if($this->isAllowedToCreatePrivateChannel()) { - array_push($channels, $this->getPrivateChannelID()); - } - // Add the invitation channels: - foreach($this->getInvitations() as $channelID) { - if(!in_array($channelID, $channels)) { - array_push($channels, $channelID); - } - } - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/who '.implode(' ', $this->getOnlineUsers($channels)) - ); - } - } else { - $channelName = $textParts[1]; - $channelID = $this->getChannelIDFromChannelName($channelName); - if(!$this->validateChannel($channelID)) { - // Invalid channel: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error InvalidChannelName '.$channelName - ); - } else { - // Get online users for the given channel: - $onlineUsers = $this->getOnlineUsers(array($channelID)); - if(count($onlineUsers) > 0) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/whoChannel '.$channelName.' '.implode(' ', $onlineUsers) - ); - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/whoEmpty -' - ); - } - } - } - } - - function insertParsedMessageList($textParts) { - // Get the names of all accessible channels: - $channelNames = $this->getChannelNames(); - // Add the own private channel, if allowed: - if($this->isAllowedToCreatePrivateChannel()) { - array_push($channelNames, $this->getPrivateChannelName()); - } - // Add the invitation channels: - foreach($this->getInvitations() as $channelID) { - $channelName = $this->getChannelNameFromChannelID($channelID); - if($channelName !== null && !in_array($channelName, $channelNames)) { - array_push($channelNames, $channelName); - } - } - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/list '.implode(' ', $channelNames) - ); - } - - function insertParsedMessageWhereis($textParts) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - // Get UserID from UserName: - $whereisUserID = $this->getIDFromName($textParts[1]); - if($whereisUserID === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - $channelID = $this->getChannelFromID($whereisUserID); - if($this->validateChannel($channelID)) { - $channelName = $this->getChannelNameFromChannelID($channelID); - } else { - $channelName = null; - } - if($channelName === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // List user information: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/whereis '.$textParts[1].' '.$channelName - ); - } - } - } - } - - function insertParsedMessageWhois($textParts) { - // Only moderators/admins: - if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { - if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - // Get UserID from UserName: - $whoisUserID = $this->getIDFromName($textParts[1]); - if($whoisUserID === null) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameNotFound '.$textParts[1] - ); - } else { - // List user information: - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/whois '.$textParts[1].' '.$this->getIPFromID($whoisUserID) - ); - } - } - } else { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error CommandNotAllowed '.$textParts[0] - ); - } - } - - function insertParsedMessageRoll($textParts) { - if(count($textParts) == 1) { - // default is one d6: - $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); - } else { - $diceParts = explode('d', $textParts[1]); - if(count($diceParts) == 2) { - $number = (int)$diceParts[0]; - $sides = (int)$diceParts[1]; - - // Dice number must be an integer between 1 and 100, else roll only one: - $number = ($number > 0 && $number <= 100) ? $number : 1; - - // Sides must be an integer between 1 and 100, else take 6: - $sides = ($sides > 0 && $sides <= 100) ? $sides : 6; - - $text = '/roll '.$this->getUserName().' '.$number.'d'.$sides.' '; - for($i=0; $i<$number; $i++) { - if($i != 0) - $text .= ','; - $text .= $this->rollDice($sides); - } - } else { - // if dice syntax is invalid, roll one d6: - $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); - } - } - $this->insertChatBotMessage( - $this->getChannel(), - $text - ); - } - - function insertParsedMessageNick($textParts) { - if(!$this->getConfig('allowNickChange') || - (!$this->getConfig('allowGuestUserName') && $this->getUserRole() == AJAX_CHAT_GUEST)) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error CommandNotAllowed '.$textParts[0] - ); - } else if(count($textParts) == 1) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MissingUserName' - ); - } else { - $newUserName = implode(' ', array_slice($textParts, 1)); - if($newUserName == $this->getLoginUserName()) { - // Allow the user to regain the original login userName: - $prefix = ''; - $suffix = ''; - } else if($this->getUserRole() == AJAX_CHAT_GUEST) { - $prefix = $this->getConfig('guestUserPrefix'); - $suffix = $this->getConfig('guestUserSuffix'); - } else { - $prefix = $this->getConfig('changedNickPrefix'); - $suffix = $this->getConfig('changedNickSuffix'); - } - $maxLength = $this->getConfig('userNameMaxLength') - - $this->stringLength($prefix) - - $this->stringLength($suffix); - $newUserName = $this->trimString($newUserName, 'UTF-8', $maxLength, true); - if(!$newUserName) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error InvalidUserName' - ); - } else { - $newUserName = $prefix.$newUserName.$suffix; - if($this->isUserNameInUse($newUserName)) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error UserNameInUse' - ); - } else { - $oldUserName = $this->getUserName(); - $this->setUserName($newUserName); - $this->updateOnlineList(); - // Add info message to update the client-side stored userName: - $this->addInfoMessage($this->getUserName(), 'userName'); - $this->insertChatBotMessage( - $this->getChannel(), - '/nick '.$oldUserName.' '.$newUserName, - null, - 2 - ); - } - } - } - } - - function insertMessage($text) { - if(!$this->isAllowedToWriteMessage()) - return; - - if(!$this->floodControl()) - return; - - $text = $this->trimMessageText($text); - if($text == '') - return; - - if(!$this->onNewMessage($text)) - return; - - $text = $this->replaceCustomText($text); - - $this->insertParsedMessage($text); - } - - function deleteMessage($messageID) { - // Retrieve the channel of the given message: - $sql = 'SELECT - channel - FROM - '.$this->getDataBaseTable('messages').' - WHERE - id='.$this->db->makeSafe($messageID).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $row = $result->fetch(); - - if($row['channel'] !== null) { - $channel = $row['channel']; - - if($this->getUserRole() == AJAX_CHAT_ADMIN) { - $condition = ''; - } else if($this->getUserRole() == AJAX_CHAT_MODERATOR) { - $condition = ' AND - NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') - AND - NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).')'; - } else if($this->getUserRole() == AJAX_CHAT_USER && $this->getConfig('allowUserMessageDelete')) { - $condition = 'AND - ( - userID='.$this->db->makeSafe($this->getUserID()).' - OR - ( - channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' - OR - channel = '.$this->db->makeSafe($this->getPrivateChannelID()).' - ) - AND - NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') - AND - NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).') - )'; - } else { - return false; - } - - // Remove given message from the database: - $sql = 'DELETE FROM - '.$this->getDataBaseTable('messages').' - WHERE - id='.$this->db->makeSafe($messageID).' - '.$condition.';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - if($result->affectedRows() == 1) { - // Insert a deletion command to remove the message from the clients chatlists: - $this->insertChatBotMessage($channel, '/delete '.$messageID); - return true; - } - } - return false; - } - - function floodControl() { - // Moderators and Admins need no flood control: - if($this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == AJAX_CHAT_ADMIN) { - return true; - } - $time = time(); - // Check the time of the last inserted message: - if($this->getInsertedMessagesRateTimeStamp()+60 < $time) { - $this->setInsertedMessagesRateTimeStamp($time); - $this->setInsertedMessagesRate(1); - } else { - // Increase the inserted messages rate: - $rate = $this->getInsertedMessagesRate()+1; - $this->setInsertedMessagesRate($rate); - // Check if message rate is too high: - if($rate > $this->getConfig('maxMessageRate')) { - $this->insertChatBotMessage( - $this->getPrivateMessageID(), - '/error MaxMessageRate' - ); - // Return false so the message is not inserted: - return false; - } - } - return true; - } - - function isAllowedToWriteMessage() { - if($this->getUserRole() != AJAX_CHAT_GUEST) - return true; - if($this->getConfig('allowGuestWrite')) - return true; - return false; - } - - function insertChatBotMessage($channelID, $messageText, $ip=null, $mode=0) { - $this->insertCustomMessage( - $this->getConfig('chatBotID'), - $this->getConfig('chatBotName'), - AJAX_CHAT_CHATBOT, - $channelID, - $messageText, - $ip, - $mode - ); - } - - function insertCustomMessage($userID, $userName, $userRole, $channelID, $text, $ip=null, $mode=0) { - // The $mode parameter is used for socket updates: - // 0 = normal messages - // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) - // 2 = messages with online user updates (nick) - - $ip = $ip ? $ip : $_SERVER['REMOTE_ADDR']; - - $sql = 'INSERT INTO '.$this->getDataBaseTable('messages').'( - userID, - userName, - userRole, - channel, - dateTime, - ip, - text - ) - VALUES ( - '.$this->db->makeSafe($userID).', - '.$this->db->makeSafe($userName).', - '.$this->db->makeSafe($userRole).', - '.$this->db->makeSafe($channelID).', - NOW(), - '.$this->db->makeSafe($this->ipToStorageFormat($ip)).', - '.$this->db->makeSafe($text).' - );'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - if($this->getConfig('socketServerEnabled')) { - $this->sendSocketMessage( - $this->getSocketBroadcastMessage( - $this->db->getLastInsertedID(), - time(), - $userID, - $userName, - $userRole, - $channelID, - $text, - $mode - ) - ); - } - } - - function getSocketBroadcastMessage( - $messageID, - $timeStamp, - $userID, - $userName, - $userRole, - $channelID, - $text, - $mode - ) { - // The $mode parameter: - // 0 = normal messages - // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) - // 2 = messages with online user updates (nick) - - // Get the message XML content: - $xml = ''; - if($mode) { - // Add the list of online users if the user list has been updated ($mode > 0): - $xml .= $this->getChatViewOnlineUsersXML(array($channelID)); - } - if($mode != 1 || $this->getConfig('showChannelMessages')) { - $xml .= ''; - $xml .= $this->getChatViewMessageXML( - $messageID, - $timeStamp, - $userID, - $userName, - $userRole, - $channelID, - $text - ); - $xml .= ''; - } - $xml .= ''; - return $xml; - } - - function sendSocketMessage($message) { - // Open a TCP socket connection to the socket server: - if($socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) { - if(@socket_connect($socket, $this->getConfig('socketServerIP'), $this->getConfig('socketServerPort'))) { - // Append a null-byte to the string as EOL (End Of Line) character - // which is required by Flash XML socket communication: - $message .= "\0"; - @socket_write( - $socket, - $message, - strlen($message) // Using strlen to count the bytes instead of the number of UTF-8 characters - ); - } - @socket_close($socket); - } - } - - function updateSocketAuthentication($userID, $socketRegistrationID=null, $channels=null) { - // If no $socketRegistrationID or no $channels are given the authentication is removed for the given user: - $authentication = ''; - if($channels) { - foreach($channels as $channelID) { - $authentication .= ''; - } - } - $authentication .= ''; - $this->sendSocketMessage($authentication); - } - - function setSocketRegistrationID($value) { - $this->setSessionVar('SocketRegistrationID', $value); - } - - function getSocketRegistrationID() { - return $this->getSessionVar('SocketRegistrationID'); - } - - function rollDice($sides) { - // seed with microseconds since last "whole" second: - mt_srand((double)microtime()*1000000); - - return mt_rand(1, $sides); - } - - function kickUser($userName, $banMinutes=null, $userID=null) { - if($userID === null) { - $userID = $this->getIDFromName($userName); - } - if($userID === null) { - return; - } - - $banMinutes = $banMinutes ? $banMinutes : $this->getConfig('defaultBanTime'); - - if($banMinutes) { - // Ban User for the given time in minutes: - $this->banUser($userName, $banMinutes, $userID); - } - - // Remove given User from online list: - $sql = 'DELETE FROM - '.$this->getDataBaseTable('online').' - WHERE - userID = '.$this->db->makeSafe($userID).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - // Update the socket server authentication for the kicked user: - if($this->getConfig('socketServerEnabled')) { - $this->updateSocketAuthentication($userID); - } - - $this->removeUserFromOnlineUsersData($userID); - } - - function getBannedUsersData($key=null, $value=null) { - if($this->_bannedUsersData === null) { - $this->_bannedUsersData = array(); - - $sql = 'SELECT - userID, - userName, - ip - FROM - '.$this->getDataBaseTable('bans').' - WHERE - NOW() < dateTime;'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - while($row = $result->fetch()) { - $row['ip'] = $this->ipFromStorageFormat($row['ip']); - array_push($this->_bannedUsersData, $row); - } - - $result->free(); - } - - if($key) { - $bannedUsersData = array(); - foreach($this->_bannedUsersData as $bannedUserData) { - if(!isset($bannedUserData[$key])) { - return $bannedUsersData; - } - if($value) { - if($bannedUserData[$key] == $value) { - array_push($bannedUsersData, $bannedUserData); - } else { - continue; - } - } else { - array_push($bannedUsersData, $bannedUserData[$key]); - } - } - return $bannedUsersData; - } - - return $this->_bannedUsersData; - } - - function getBannedUsers() { - return $this->getBannedUsersData('userName'); - } - - function banUser($userName, $banMinutes=null, $userID=null) { - if($userID === null) { - $userID = $this->getIDFromName($userName); - } - $ip = $this->getIPFromID($userID); - if(!$ip || $userID === null) { - return; - } - - // Remove expired bans: - $this->removeExpiredBans(); - - $banMinutes = (int)$banMinutes; - if(!$banMinutes) { - // If banMinutes is not a valid integer, use the defaultBanTime: - $banMinutes = $this->getConfig('defaultBanTime'); - } - - $sql = 'INSERT INTO '.$this->getDataBaseTable('bans').'( - userID, - userName, - dateTime, - ip - ) - VALUES ( - '.$this->db->makeSafe($userID).', - '.$this->db->makeSafe($userName).', - DATE_ADD(NOW(), interval '.$this->db->makeSafe($banMinutes).' MINUTE), - '.$this->db->makeSafe($this->ipToStorageFormat($ip)).' - );'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function unbanUser($userName) { - $sql = 'DELETE FROM - '.$this->getDataBaseTable('bans').' - WHERE - userName = '.$this->db->makeSafe($userName).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function removeExpiredBans() { - $sql = 'DELETE FROM - '.$this->getDataBaseTable('bans').' - WHERE - dateTime < NOW();'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function setInactive($userID, $userName=null) { - $condition = 'userID='.$this->db->makeSafe($userID); - if($userName !== null) { - $condition .= ' OR userName='.$this->db->makeSafe($userName); - } - $sql = 'UPDATE - '.$this->getDataBaseTable('online').' - SET - dateTime = DATE_SUB(NOW(), interval '.(intval($this->getConfig('inactiveTimeout'))+1).' MINUTE) - WHERE - '.$condition.';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $this->resetOnlineUsersData(); - } - - function removeInactive() { - $sql = 'SELECT - userID, - userName, - channel - FROM - '.$this->getDataBaseTable('online').' - WHERE - NOW() > DATE_ADD(dateTime, interval '.$this->getConfig('inactiveTimeout').' MINUTE);'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - if($result->numRows() > 0) { - $condition = ''; - while($row = $result->fetch()) { - if(!empty($condition)) - $condition .= ' OR '; - // Add userID to condition for removal: - $condition .= 'userID='.$this->db->makeSafe($row['userID']); - - // Update the socket server authentication for the kicked user: - if($this->getConfig('socketServerEnabled')) { - $this->updateSocketAuthentication($row['userID']); - } - - $this->removeUserFromOnlineUsersData($row['userID']); - - // Insert logout timeout message: - $text = '/logout '.$row['userName'].' Timeout'; - $this->insertChatBotMessage( - $row['channel'], - $text, - null, - 1 - ); - } - - $result->free(); - - $sql = 'DELETE FROM - '.$this->getDataBaseTable('online').' - WHERE - '.$condition.';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - } - - function updateOnlineStatus() { - // Update online status every 50 seconds (this allows update requests to be in time): - if(!$this->getStatusUpdateTimeStamp() || ((time() - $this->getStatusUpdateTimeStamp()) > 50)) { - $this->updateOnlineList(); - $this->setStatusUpdateTimeStamp(time()); - } - } - - function checkAndRemoveInactive() { - // Remove inactive users every inactiveCheckInterval: - if(!$this->getInactiveCheckTimeStamp() || ((time() - $this->getInactiveCheckTimeStamp()) > $this->getConfig('inactiveCheckInterval')*60)) { - $this->removeInactive(); - $this->setInactiveCheckTimeStamp(time()); - } - } - - function sendXMLMessages() { - $httpHeader = new AJAXChatHTTPHeader('UTF-8', 'text/xml'); - - // Send HTTP header: - $httpHeader->send(); - - // Output XML messages: - echo $this->getXMLMessages(); - } - - function getXMLMessages() { - switch($this->getView()) { - case 'chat': - return $this->getChatViewXMLMessages(); - case 'teaser': - return $this->getTeaserViewXMLMessages(); - case 'logs': - return $this->getLogsViewXMLMessages(); - default: - return $this->getLogoutXMLMessage(); - } - } - - function getMessageCondition() { - $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')).' - AND ( - channel = '.$this->db->makeSafe($this->getChannel()).' - OR - channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' - ) - AND - '; - if($this->getConfig('requestMessagesPriorChannelEnter') || - ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($this->getChannel(), $this->getConfig('requestMessagesPriorChannelEnterList')))) { - $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; - } else { - $condition .= 'dateTime >= FROM_UNIXTIME(' . $this->getChannelEnterTimeStamp() . ')'; - } - return $condition; - } - - function getMessageFilter() { - $filterChannelMessages = ''; - if(!$this->getConfig('showChannelMessages') || $this->getRequestVar('shoutbox')) { - $filterChannelMessages = ' AND NOT ( - text LIKE (\'/login%\') - OR - text LIKE (\'/logout%\') - OR - text LIKE (\'/channelEnter%\') - OR - text LIKE (\'/channelLeave%\') - OR - text LIKE (\'/kick%\') - )'; - } - return $filterChannelMessages; - } - - function getInfoMessagesXML() { - $xml = ''; - // Go through the info messages: - foreach($this->getInfoMessages() as $type=>$infoArray) { - foreach($infoArray as $info) { - $xml .= ''; - $xml .= 'encodeSpecialChars($info).']]>'; - $xml .= ''; - } - } - $xml .= ''; - return $xml; - } - - function getChatViewOnlineUsersXML($channelIDs) { - // Get the online users for the given channels: - $onlineUsersData = $this->getOnlineUsersData($channelIDs); - $xml = ''; - foreach($onlineUsersData as $onlineUserData) { - $xml .= 'encodeSpecialChars($onlineUserData['userName']).']]>'; - $xml .= ''; - } - $xml .= ''; - return $xml; - } - - function getLogoutXMLMessage() { - $xml = ''; - $xml .= ''; - $xml .= ''; - $xml .= ''; - $xml .= 'encodeSpecialChars($this->getConfig('logoutData')).']]>'; - $xml .= ''; - $xml .= ''; - $xml .= ''; - return $xml; - } - - function getChatViewMessageXML( - $messageID, - $timeStamp, - $userID, - $userName, - $userRole, - $channelID, - $text - ) { - $message = 'encodeSpecialChars($userName).']]>'; - $message .= 'encodeSpecialChars($text).']]>'; - $message .= ''; - return $message; - } - - function getChatViewMessagesXML() { - // Get the last messages in descending order (this optimises the LIMIT usage): - $sql = 'SELECT - id, - userID, - userName, - userRole, - channel AS channelID, - UNIX_TIMESTAMP(dateTime) AS timeStamp, - text - FROM - '.$this->getDataBaseTable('messages').' - WHERE - '.$this->getMessageCondition().' - '.$this->getMessageFilter().' - ORDER BY - id - DESC - LIMIT '.$this->getConfig('requestMessagesLimit').';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $messages = ''; - - // Add the messages in reverse order so it is ascending again: - while($row = $result->fetch()) { - $message = $this->getChatViewMessageXML( - $row['id'], - $row['timeStamp'], - $row['userID'], - $row['userName'], - $row['userRole'], - $row['channelID'], - $row['text'] - ); - $messages = $message.$messages; - } - $result->free(); - - $messages = ''.$messages.''; - return $messages; - } - - function getChatViewXMLMessages() { - $xml = ''; - $xml .= ''; - $xml .= $this->getInfoMessagesXML(); - $xml .= $this->getChatViewOnlineUsersXML(array($this->getChannel())); - $xml .= $this->getChatViewMessagesXML(); - $xml .= ''; - return $xml; - } - - function getTeaserMessageCondition() { - $channelID = $this->getValidRequestChannelID(); - $condition = 'channel = '.$this->db->makeSafe($channelID).' - AND - '; - if($this->getConfig('requestMessagesPriorChannelEnter') || - ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($channelID, $this->getConfig('requestMessagesPriorChannelEnterList')))) { - $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; - } else { - // Teaser content may not be shown for this channel: - $condition .= '0 = 1'; - } - return $condition; - } - - function getTeaserViewMessagesXML() { - // Get the last messages in descending order (this optimises the LIMIT usage): - $sql = 'SELECT - id, - userID, - userName, - userRole, - channel AS channelID, - UNIX_TIMESTAMP(dateTime) AS timeStamp, - text - FROM - '.$this->getDataBaseTable('messages').' - WHERE - '.$this->getTeaserMessageCondition().' - '.$this->getMessageFilter().' - ORDER BY - id - DESC - LIMIT '.$this->getConfig('requestMessagesLimit').';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $messages = ''; - - // Add the messages in reverse order so it is ascending again: - while($row = $result->fetch()) { - $message = ''; - $message .= 'encodeSpecialChars($row['userName']).']]>'; - $message .= 'encodeSpecialChars($row['text']).']]>'; - $message .= ''; - $messages = $message.$messages; - } - $result->free(); - - $messages = ''.$messages.''; - return $messages; - } - - function getTeaserViewXMLMessages() { - $xml = ''; - $xml .= ''; - $xml .= $this->getInfoMessagesXML(); - $xml .= $this->getTeaserViewMessagesXML(); - $xml .= ''; - return $xml; - } - - function getLogsViewCondition() { - $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')); - - // Check the channel condition: - switch($this->getRequestVar('channelID')) { - case '-3': - // Just display messages from all accessible channels - if($this->getUserRole() != AJAX_CHAT_ADMIN) { - $condition .= ' AND (channel = '.$this->db->makeSafe($this->getPrivateMessageID()); - $condition .= ' OR channel = '.$this->db->makeSafe($this->getPrivateChannelID()); - foreach($this->getChannels() as $channel) { - if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { - continue; - } - $condition .= ' OR channel = '.$this->db->makeSafe($channel); - } - $condition .= ')'; - } - break; - case '-2': - if($this->getUserRole() != AJAX_CHAT_ADMIN) { - $condition .= ' AND channel = '.($this->getPrivateMessageID()); - } else { - $condition .= ' AND channel > '.($this->getConfig('privateMessageDiff')-1); - } - break; - case '-1': - if($this->getUserRole() != AJAX_CHAT_ADMIN) { - $condition .= ' AND channel = '.($this->getPrivateChannelID()); - } else { - $condition .= ' AND (channel > '.($this->getConfig('privateChannelDiff')-1).' AND channel < '.($this->getConfig('privateMessageDiff')).')'; - } - break; - default: - if(($this->getUserRole() == AJAX_CHAT_ADMIN || !$this->getConfig('logsUserAccessChannelList') || in_array($this->getRequestVar('channelID'), $this->getConfig('logsUserAccessChannelList'))) - && $this->validateChannel($this->getRequestVar('channelID'))) { - $condition .= ' AND channel = '.$this->db->makeSafe($this->getRequestVar('channelID')); - } else { - // No valid channel: - $condition .= ' AND 0 = 1'; - } - } - - // Check the period condition: - $hour = ($this->getRequestVar('hour') === null || $this->getRequestVar('hour') > 23 || $this->getRequestVar('hour') < 0) ? null : $this->getRequestVar('hour'); - $day = ($this->getRequestVar('day') === null || $this->getRequestVar('day') > 31 || $this->getRequestVar('day') < 1) ? null : $this->getRequestVar('day'); - $month = ($this->getRequestVar('month') === null || $this->getRequestVar('month') > 12 || $this->getRequestVar('month') < 1) ? null : $this->getRequestVar('month'); - $year = ($this->getRequestVar('year') === null || $this->getRequestVar('year') > date('Y') || $this->getRequestVar('year') < $this->getConfig('logsFirstYear')) ? null : $this->getRequestVar('year'); - - // If a time (hour) is given but no date (year, month, day), use the current date: - if($hour !== null) { - if($day === null) - $day = date('j'); - if($month === null) - $month = date('n'); - if($year === null) - $year = date('Y'); - } - - if($year === null) { - // No year given, so no period condition - } else if($month === null) { - // Define the given year as period: - $periodStart = mktime(0, 0, 0, 1, 1, $year); - // The last day in a month can be expressed by using 0 for the day of the next month: - $periodEnd = mktime(23, 59, 59, 13, 0, $year); - } else if($day === null) { - // Define the given month as period: - $periodStart = mktime(0, 0, 0, $month, 1, $year); - // The last day in a month can be expressed by using 0 for the day of the next month: - $periodEnd = mktime(23, 59, 59, $month+1, 0, $year); - } else if($hour === null){ - // Define the given day as period: - $periodStart = mktime(0, 0, 0, $month, $day, $year); - $periodEnd = mktime(23, 59, 59, $month, $day, $year); - } else { - // Define the given hour as period: - $periodStart = mktime($hour, 0, 0, $month, $day, $year); - $periodEnd = mktime($hour, 59, 59, $month, $day, $year); - } - - if(isset($periodStart)) - $condition .= ' AND dateTime > \''.date('Y-m-d H:i:s', $periodStart).'\' AND dateTime <= \''.date('Y-m-d H:i:s', $periodEnd).'\''; - - // Check the search condition: - if($this->getRequestVar('search')) { - if(($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) && strpos($this->getRequestVar('search'), 'ip=') === 0) { - // Search for messages with the given IP: - $ip = substr($this->getRequestVar('search'), 3); - $condition .= ' AND (ip = '.$this->db->makeSafe($this->ipToStorageFormat($ip)).')'; - } else if(strpos($this->getRequestVar('search'), 'userID=') === 0) { - // Search for messages with the given userID: - $userID = substr($this->getRequestVar('search'), 7); - $condition .= ' AND (userID = '.$this->db->makeSafe($userID).')'; - } else { - // Use the search value as regular expression on message text and username: - $condition .= ' AND (userName REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).' OR text REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).')'; - } - } - - // If no period or search condition is given, just monitor the last messages on the given channel: - if(!isset($periodStart) && !$this->getRequestVar('search')) { - $condition .= ' AND NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('logsRequestMessagesTimeDiff').' HOUR)'; - } - - return $condition; - } - - function getLogsViewMessagesXML() { - $sql = 'SELECT - id, - userID, - userName, - userRole, - channel AS channelID, - UNIX_TIMESTAMP(dateTime) AS timeStamp, - ip, - text - FROM - '.$this->getDataBaseTable('messages').' - WHERE - '.$this->getLogsViewCondition().' - ORDER BY - id - LIMIT '.$this->getConfig('logsRequestMessagesLimit').';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - $xml = ''; - while($row = $result->fetch()) { - $xml .= 'getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { - $xml .= ' ip="'.$this->ipFromStorageFormat($row['ip']).'"'; - } - $xml .= '>'; - $xml .= 'encodeSpecialChars($row['userName']).']]>'; - $xml .= 'encodeSpecialChars($row['text']).']]>'; - $xml .= ''; - } - $result->free(); - - $xml .= ''; - - return $xml; - } - - function getLogsViewXMLMessages() { - $xml = ''; - $xml .= ''; - $xml .= $this->getInfoMessagesXML(); - $xml .= $this->getLogsViewMessagesXML(); - $xml .= ''; - return $xml; - } - - function purgeLogs() { - $sql = 'DELETE FROM - '.$this->getDataBaseTable('messages').' - WHERE - dateTime < DATE_SUB(NOW(), interval '.$this->getConfig('logsPurgeTimeDiff').' DAY);'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function getInfoMessages($type=null) { - if(!isset($this->_infoMessages)) { - $this->_infoMessages = array(); - } - if($type) { - if(!isset($this->_infoMessages[$type])) { - $this->_infoMessages[$type] = array(); - } - return $this->_infoMessages[$type]; - } else { - return $this->_infoMessages; - } - } - - function addInfoMessage($info, $type='error') { - if(!isset($this->_infoMessages)) { - $this->_infoMessages = array(); - } - if(!isset($this->_infoMessages[$type])) { - $this->_infoMessages[$type] = array(); - } - if(!in_array($info, $this->_infoMessages[$type])) { - array_push($this->_infoMessages[$type], $info); - } - } - - function getRequestVars() { - return $this->_requestVars; - } - - function getRequestVar($key) { - if($this->_requestVars && isset($this->_requestVars[$key])) { - return $this->_requestVars[$key]; - } - return null; - } - - function setRequestVar($key, $value) { - if(!$this->_requestVars) { - $this->_requestVars = array(); - } - $this->_requestVars[$key] = $value; - } - - function getOnlineUsersData($channelIDs=null, $key=null, $value=null) { - if($this->_onlineUsersData === null) { - $this->_onlineUsersData = array(); - - $sql = 'SELECT - userID, - userName, - userRole, - channel, - UNIX_TIMESTAMP(dateTime) AS timeStamp, - ip - FROM - '.$this->getDataBaseTable('online').' - ORDER BY - userName;'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - while($row = $result->fetch()) { - $row['ip'] = $this->ipFromStorageFormat($row['ip']); - array_push($this->_onlineUsersData, $row); - } - - $result->free(); - } - - if($channelIDs || $key) { - $onlineUsersData = array(); - foreach($this->_onlineUsersData as $userData) { - if($channelIDs && !in_array($userData['channel'], $channelIDs)) { - continue; - } - if($key) { - if(!isset($userData[$key])) { - return $onlineUsersData; - } - if($value !== null) { - if($userData[$key] == $value) { - array_push($onlineUsersData, $userData); - } else { - continue; - } - } else { - array_push($onlineUsersData, $userData[$key]); - } - } else { - array_push($onlineUsersData, $userData); - } - } - return $onlineUsersData; - } - - return $this->_onlineUsersData; - } - - function removeUserFromOnlineUsersData($userID=null) { - if(!$this->_onlineUsersData) { - return; - } - $userID = ($userID === null) ? $this->getUserID() : $userID; - for($i=0; $i_onlineUsersData); $i++) { - if($this->_onlineUsersData[$i]['userID'] == $userID) { - array_splice($this->_onlineUsersData, $i, 1); - break; - } - } - } - - function resetOnlineUsersData() { - $this->_onlineUsersData = null; - } - - function getOnlineUsers($channelIDs=null) { - return $this->getOnlineUsersData($channelIDs, 'userName'); - } - - function getOnlineUserIDs($channelIDs=null) { - return $this->getOnlineUsersData($channelIDs, 'userID'); - } - - function startSession() { - if(!session_id()) { - // Set the session name: - session_name($this->getConfig('sessionName')); - - // Set session cookie parameters: - session_set_cookie_params( - 0, // The session is destroyed on logout anyway, so no use to set this - $this->getConfig('sessionCookiePath'), - $this->getConfig('sessionCookieDomain'), - $this->getConfig('sessionCookieSecure') - ); - - // Start the session: - session_start(); - - // We started a new session: - $this->_sessionNew = true; - } - } - - function destroySession() { - if($this->_sessionNew) { - // Delete all session variables: - $_SESSION = array(); - - // Delete the session cookie: - if (isset($_COOKIE[session_name()])) { - setcookie( - session_name(), - '', - time()-42000, - $this->getConfig('sessionCookiePath'), - $this->getConfig('sessionCookieDomain'), - $this->getConfig('sessionCookieSecure') - ); - } - - // Destroy the session: - session_destroy(); - } else { - // Unset all session variables starting with the sessionKeyPrefix: - foreach($_SESSION as $key=>$value) { - if(strpos($key, $this->getConfig('sessionKeyPrefix')) === 0) { - unset($_SESSION[$key]); - } - } - } - } - - function regenerateSessionID() { - if($this->_sessionNew) { - // Regenerate session id: - @session_regenerate_id(true); - } - } - - function getSessionVar($key, $prefix=null) { - if($prefix === null) - $prefix = $this->getConfig('sessionKeyPrefix'); - - // Return the session value if existing: - if(isset($_SESSION[$prefix.$key])) - return $_SESSION[$prefix.$key]; - else - return null; - } - - function setSessionVar($key, $value, $prefix=null) { - if($prefix === null) - $prefix = $this->getConfig('sessionKeyPrefix'); - - // Set the session value: - $_SESSION[$prefix.$key] = $value; - } - - function getSessionIP() { - return $this->getSessionVar('IP'); - } - - function setSessionIP($ip) { - $this->setSessionVar('IP', $ip); - } - - function getQueryUserName() { - return $this->getSessionVar('QueryUserName'); - } - - function setQueryUserName($userName) { - $this->setSessionVar('QueryUserName', $userName); - } - - function getInvitations() { - if($this->_invitations === null) { - $this->_invitations = array(); - - $sql = 'SELECT - channel - FROM - '.$this->getDataBaseTable('invitations').' - WHERE - userID='.$this->db->makeSafe($this->getUserID()).' - AND - DATE_SUB(NOW(), interval 1 DAY) < dateTime;'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - - while($row = $result->fetch()) { - array_push($this->_invitations, $row['channel']); - } - - $result->free(); - } - return $this->_invitations; - } - - function removeExpiredInvitations() { - $sql = 'DELETE FROM - '.$this->getDataBaseTable('invitations').' - WHERE - DATE_SUB(NOW(), interval 1 DAY) > dateTime;'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function addInvitation($userID, $channelID=null) { - $this->removeExpiredInvitations(); - - $channelID = ($channelID === null) ? $this->getChannel() : $channelID; - - $sql = 'INSERT INTO '.$this->getDataBaseTable('invitations').'( - userID, - channel, - dateTime - ) - VALUES ( - '.$this->db->makeSafe($userID).', - '.$this->db->makeSafe($channelID).', - NOW() - );'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function removeInvitation($userID, $channelID=null) { - $channelID = ($channelID === null) ? $this->getChannel() : $channelID; - - $sql = 'DELETE FROM - '.$this->getDataBaseTable('invitations').' - WHERE - userID='.$this->db->makeSafe($userID).' - AND - channel='.$this->db->makeSafe($channelID).';'; - - // Create a new SQL query: - $result = $this->db->sqlQuery($sql); - - // Stop if an error occurs: - if($result->error()) { - echo $result->getError(); - die(); - } - } - - function getUserID() { - return $this->getSessionVar('UserID'); - } - - function setUserID($id) { - $this->setSessionVar('UserID', $id); - } - - function getUserName() { - return $this->getSessionVar('UserName'); - } - - function setUserName($name) { - $this->setSessionVar('UserName', $name); - } - - function getLoginUserName() { - return $this->getSessionVar('LoginUserName'); - } - - function setLoginUserName($name) { - $this->setSessionVar('LoginUserName', $name); - } - - function getUserRole() { - $userRole = $this->getSessionVar('UserRole'); - if($userRole === null) - return AJAX_CHAT_GUEST; - return $userRole; - } - - function setUserRole($role) { - $this->setSessionVar('UserRole', $role); - } - - function getChannel() { - return $this->getSessionVar('Channel'); - } - - function setChannel($channel) { - $this->setSessionVar('Channel', $channel); - - // Save the channel enter timestamp: - $this->setChannelEnterTimeStamp(time()); - - // Update the channel authentication for the socket server: - if($this->getConfig('socketServerEnabled')) { - $this->updateSocketAuthentication( - $this->getUserID(), - $this->getSocketRegistrationID(), - array($channel,$this->getPrivateMessageID()) - ); - } - - // Reset the logs view socket authentication session var: - if($this->getSessionVar('logsViewSocketAuthenticated')) { - $this->setSessionVar('logsViewSocketAuthenticated', false); - } - } - - function isLoggedIn() { - return (bool)$this->getSessionVar('LoggedIn'); - } - - function setLoggedIn($bool) { - $this->setSessionVar('LoggedIn', $bool); - } - - function getLoginTimeStamp() { - return $this->getSessionVar('LoginTimeStamp'); - } - - function setLoginTimeStamp($time) { - $this->setSessionVar('LoginTimeStamp', $time); - } - - function getChannelEnterTimeStamp() { - return $this->getSessionVar('ChannelEnterTimeStamp'); - } - - function setChannelEnterTimeStamp($time) { - $this->setSessionVar('ChannelEnterTimeStamp', $time); - } - - function getStatusUpdateTimeStamp() { - return $this->getSessionVar('StatusUpdateTimeStamp'); - } - - function setStatusUpdateTimeStamp($time) { - $this->setSessionVar('StatusUpdateTimeStamp', $time); - } - - function getInactiveCheckTimeStamp() { - return $this->getSessionVar('InactiveCheckTimeStamp'); - } - - function setInactiveCheckTimeStamp($time) { - $this->setSessionVar('InactiveCheckTimeStamp', $time); - } - - function getInsertedMessagesRate() { - return $this->getSessionVar('InsertedMessagesRate'); - } - - function setInsertedMessagesRate($rate) { - $this->setSessionVar('InsertedMessagesRate', $rate); - } - - function getInsertedMessagesRateTimeStamp() { - return $this->getSessionVar('InsertedMessagesRateTimeStamp'); - } - - function setInsertedMessagesRateTimeStamp($time) { - $this->setSessionVar('InsertedMessagesRateTimeStamp', $time); - } - - function getLangCode() { - // Get the langCode from request or cookie: - $langCodeCookie = isset($_COOKIE[$this->getConfig('sessionName').'_lang']) ? $_COOKIE[$this->getConfig('sessionName').'_lang'] : null; - $langCode = $this->getRequestVar('lang') ? $this->getRequestVar('lang') : $langCodeCookie; - // Check if the langCode is valid: - if(!in_array($langCode, $this->getConfig('langAvailable'))) { - // Determine the user language: - $language = new AJAXChatLanguage($this->getConfig('langAvailable'), $this->getConfig('langDefault')); - $langCode = $language->getLangCode(); - } - return $langCode; - } - - function setLangCodeCookie() { - setcookie( - $this->getConfig('sessionName').'_lang', - $this->getLangCode(), - time()+60*60*24*$this->getConfig('sessionCookieLifeTime'), - $this->getConfig('sessionCookiePath'), - $this->getConfig('sessionCookieDomain'), - $this->getConfig('sessionCookieSecure') - ); - } - - function removeUnsafeCharacters($str) { - // Remove NO-WS-CTL, non-whitespace control characters (RFC 2822), decimal 1–8, 11–12, 14–31, and 127: - return AJAXChatEncoding::removeUnsafeCharacters($str); - } - - function subString($str, $start=0, $length=null, $encoding='UTF-8') { - return AJAXChatString::subString($str, $start, $length, $encoding); - } - - function stringLength($str, $encoding='UTF-8') { - return AJAXChatString::stringLength($str, $encoding); - } - - function trimMessageText($text) { - return $this->trimString($text, 'UTF-8', $this->getConfig('messageTextMaxLength')); - } - - function trimUserName($userName) { - return $this->trimString($userName, null, $this->getConfig('userNameMaxLength'), true, true); - } - - function trimChannelName($channelName) { - return $this->trimString($channelName, null, null, true, true); - } - - function trimString($str, $sourceEncoding=null, $maxLength=null, $replaceWhitespace=false, $decodeEntities=false, $htmlEntitiesMap=null) { - // Make sure the string contains valid unicode: - $str = $this->convertToUnicode($str, $sourceEncoding); - - // Make sure the string contains no unsafe characters: - $str = $this->removeUnsafeCharacters($str); - - // Strip whitespace from the beginning and end of the string: - $str = trim($str); - - if($replaceWhitespace) { - // Replace any whitespace in the userName with the underscore "_": - $str = preg_replace('/\s/u', '_', $str); - } - - if($decodeEntities) { - // Decode entities: - $str = $this->decodeEntities($str, 'UTF-8', $htmlEntitiesMap); - } - - if($maxLength) { - // Cut the string to the allowed length: - $str = $this->subString($str, 0, $maxLength); - } - - return $str; - } - - function convertToUnicode($str, $sourceEncoding=null) { - if($sourceEncoding === null) { - $sourceEncoding = $this->getConfig('sourceEncoding'); - } - return $this->convertEncoding($str, $sourceEncoding, 'UTF-8'); - } - - function convertFromUnicode($str, $contentEncoding=null) { - if($contentEncoding === null) { - $contentEncoding = $this->getConfig('contentEncoding'); - } - return $this->convertEncoding($str, 'UTF-8', $contentEncoding); - } - - function convertEncoding($str, $charsetFrom, $charsetTo) { - return AJAXChatEncoding::convertEncoding($str, $charsetFrom, $charsetTo); - } - - function encodeEntities($str, $encoding='UTF-8', $convmap=null) { - return AJAXChatEncoding::encodeEntities($str, $encoding, $convmap); - } - - function decodeEntities($str, $encoding='UTF-8', $htmlEntitiesMap=null) { - return AJAXChatEncoding::decodeEntities($str, $encoding, $htmlEntitiesMap); - } - - function htmlEncode($str) { - return AJAXChatEncoding::htmlEncode($str, $this->getConfig('contentEncoding')); - } - - function encodeSpecialChars($str) { - return AJAXChatEncoding::encodeSpecialChars($str); - } - - function decodeSpecialChars($str) { - return AJAXChatEncoding::decodeSpecialChars($str); - } - - function ipToStorageFormat($ip) { - if(function_exists('inet_pton')) { - // ipv4 & ipv6: - return @inet_pton($ip); - } - // Only ipv4: - return @pack('N',@ip2long($ip)); - } - - function ipFromStorageFormat($ip) { - if(function_exists('inet_ntop')) { - // ipv4 & ipv6: - return @inet_ntop($ip); - } - // Only ipv4: - $unpacked = @unpack('Nlong',$ip); - if(isset($unpacked['long'])) { - return @long2ip($unpacked['long']); - } - return null; - } - - function getConfig($key, $subkey=null) { - if($subkey) - return $this->_config[$key][$subkey]; - else - return $this->_config[$key]; - } - - function setConfig($key, $subkey, $value) { - if($subkey) { - if(!isset($this->_config[$key])) { - $this->_config[$key] = array(); - } - $this->_config[$key][$subkey] = $value; - } else { - $this->_config[$key] = $value; - } - } - - function getLang($key=null) { - if(!$this->_lang) { - // Include the language file: - $lang = null; - require(AJAX_CHAT_PATH.'lib/lang/'.$this->getLangCode().'.php'); - $this->_lang = &$lang; - } - if($key === null) - return $this->_lang; - if(isset($this->_lang[$key])) - return $this->_lang[$key]; - return null; - } - - function getChatURL() { - if(defined('AJAX_CHAT_URL')) { - return AJAX_CHAT_URL; - } - - return - (isset($_SERVER['HTTPS']) ? 'https://' : 'http://'). - (isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : ''). - (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME']. - (isset($_SERVER['HTTPS']) && $_SERVER['SERVER_PORT'] == 443 || $_SERVER['SERVER_PORT'] == 80 ? '' : ':'.$_SERVER['SERVER_PORT']))). - substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/')+1); - } - - function getIDFromName($userName) { - $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); - if($userDataArray && isset($userDataArray[0])) { - return $userDataArray[0]['userID']; - } - return null; - } - - function getNameFromID($userID) { - $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); - if($userDataArray && isset($userDataArray[0])) { - return $userDataArray[0]['userName']; - } - return null; - } - - function getChannelFromID($userID) { - $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); - if($userDataArray && isset($userDataArray[0])) { - return $userDataArray[0]['channel']; - } - return null; - } - - function getIPFromID($userID) { - $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); - if($userDataArray && isset($userDataArray[0])) { - return $userDataArray[0]['ip']; - } - return null; - } - - function getRoleFromID($userID) { - $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); - if($userDataArray && isset($userDataArray[0])) { - return $userDataArray[0]['userRole']; - } - return null; - } - - function getChannelNames() { - return array_flip($this->getChannels()); - } - - function getChannelIDFromChannelName($channelName) { - if(!$channelName) - return null; - $channels = $this->getAllChannels(); - if(array_key_exists($channelName,$channels)) { - return $channels[$channelName]; - } - $channelID = null; - // Check if the requested channel is the own private channel: - if($channelName == $this->getPrivateChannelName()) { - return $this->getPrivateChannelID(); - } - // Try to retrieve a private room ID: - $strlenChannelName = $this->stringLength($channelName); - $strlenPrefix = $this->stringLength($this->getConfig('privateChannelPrefix')); - $strlenSuffix = $this->stringLength($this->getConfig('privateChannelSuffix')); - if($this->subString($channelName,0,$strlenPrefix) == $this->getConfig('privateChannelPrefix') - && $this->subString($channelName,$strlenChannelName-$strlenSuffix) == $this->getConfig('privateChannelSuffix')) { - $userName = $this->subString( - $channelName, - $strlenPrefix, - $strlenChannelName-($strlenPrefix+$strlenSuffix) - ); - $userID = $this->getIDFromName($userName); - if($userID !== null) { - $channelID = $this->getPrivateChannelID($userID); - } - } - return $channelID; - } - - function getChannelNameFromChannelID($channelID) { - foreach($this->getAllChannels() as $key=>$value) { - if($value == $channelID) { - return $key; - } - } - // Try to retrieve a private room name: - if($channelID == $this->getPrivateChannelID()) { - return $this->getPrivateChannelName(); - } - $userName = $this->getNameFromID($channelID-$this->getConfig('privateChannelDiff')); - if($userName === null) { - return null; - } - return $this->getPrivateChannelName($userName); - } - - function getChannelName() { - return $this->getChannelNameFromChannelID($this->getChannel()); - } - - function getPrivateChannelName($userName=null) { - if($userName === null) { - $userName = $this->getUserName(); - } - return $this->getConfig('privateChannelPrefix').$userName.$this->getConfig('privateChannelSuffix'); - } - - function getPrivateChannelID($userID=null) { - if($userID === null) { - $userID = $this->getUserID(); - } - return $userID + $this->getConfig('privateChannelDiff'); - } - - function getPrivateMessageID($userID=null) { - if($userID === null) { - $userID = $this->getUserID(); - } - return $userID + $this->getConfig('privateMessageDiff'); - } - - function isAllowedToSendPrivateMessage() { - if($this->getConfig('allowPrivateMessages') || $this->getUserRole() == AJAX_CHAT_ADMIN) { - return true; - } - return false; - } - - function isAllowedToCreatePrivateChannel() { - if($this->getConfig('allowPrivateChannels')) { - switch($this->getUserRole()) { - case AJAX_CHAT_USER: - return true; - case AJAX_CHAT_MODERATOR: - return true; - case AJAX_CHAT_ADMIN: - return true; - default: - return false; - } - } - return false; - } - - function isAllowedToListHiddenUsers() { - // Hidden users are users within private or restricted channels: - switch($this->getUserRole()) { - case AJAX_CHAT_MODERATOR: - return true; - case AJAX_CHAT_ADMIN: - return true; - default: - return false; - } - } - - function isUserOnline($userID=null) { - $userID = ($userID === null) ? $this->getUserID() : $userID; - $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); - if($userDataArray && count($userDataArray) > 0) { - return true; - } - return false; - } - - function isUserNameInUse($userName=null) { - $userName = ($userName === null) ? $this->getUserName() : $userName; - $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); - if($userDataArray && count($userDataArray) > 0) { - return true; - } - return false; - } - - function isUserBanned($userName, $userID=null, $ip=null) { - if($userID !== null) { - $bannedUserDataArray = $this->getBannedUsersData('userID',$userID); - if($bannedUserDataArray && isset($bannedUserDataArray[0])) { - return true; - } - } - if($ip !== null) { - $bannedUserDataArray = $this->getBannedUsersData('ip',$ip); - if($bannedUserDataArray && isset($bannedUserDataArray[0])) { - return true; - } - } - $bannedUserDataArray = $this->getBannedUsersData('userName',$userName); - if($bannedUserDataArray && isset($bannedUserDataArray[0])) { - return true; - } - return false; - } - - function isMaxUsersLoggedIn() { - if(count($this->getOnlineUsersData()) >= $this->getConfig('maxUsersLoggedIn')) { - return true; - } - return false; - } - - function validateChannel($channelID) { - if($channelID === null) { - return false; - } - // Return true for normal channels the user has acces to: - if(in_array($channelID, $this->getChannels())) { - return true; - } - // Return true if the user is allowed to join his own private channel: - if($channelID == $this->getPrivateChannelID() && $this->isAllowedToCreatePrivateChannel()) { - return true; - } - // Return true if the user has been invited to a restricted or private channel: - if(in_array($channelID, $this->getInvitations())) { - return true; - } - // No valid channel, return false: - return false; - } - - function createGuestUserName() { - $maxLength = $this->getConfig('userNameMaxLength') - - $this->stringLength($this->getConfig('guestUserPrefix')) - - $this->stringLength($this->getConfig('guestUserSuffix')); - - // seed with microseconds since last "whole" second: - mt_srand((double)microtime()*1000000); - - // Create a random userName using numbers between 100000 and 999999: - $userName = substr(mt_rand(100000, 999999), 0, $maxLength); - - return $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); - } - - // Guest userIDs must not interfere with existing userIDs and must be lower than privateChannelDiff: - function createGuestUserID() { - // seed with microseconds since last "whole" second: - mt_srand((double)microtime()*1000000); - - return mt_rand($this->getConfig('minGuestUserID'), $this->getConfig('privateChannelDiff')-1); - } - - function getGuestUser() { - if(!$this->getConfig('allowGuestLogins')) - return null; - - if($this->getConfig('allowGuestUserName')) { - $maxLength = $this->getConfig('userNameMaxLength') - - $this->stringLength($this->getConfig('guestUserPrefix')) - - $this->stringLength($this->getConfig('guestUserSuffix')); - - // Trim guest userName: - $userName = $this->trimString($this->getRequestVar('userName'), null, $maxLength, true, true); - - // If given userName is invalid, create one: - if(!$userName) { - $userName = $this->createGuestUserName(); - } else { - // Add the guest users prefix and suffix to the given userName: - $userName = $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); - } - } else { - $userName = $this->createGuestUserName(); - } - - $userData = array(); - $userData['userID'] = $this->createGuestUserID(); - $userData['userName'] = $userName; - $userData['userRole'] = AJAX_CHAT_GUEST; - return $userData; - } - - function getCustomVar($key) { - if(!isset($this->_customVars)) - $this->_customVars = array(); - if(!isset($this->_customVars[$key])) - return null; - return $this->_customVars[$key]; - } - - function setCustomVar($key, $value) { - if(!isset($this->_customVars)) - $this->_customVars = array(); - $this->_customVars[$key] = $value; - } - - // Override to replace custom template tags: - // Return the replacement for the given tag (and given tagContent) - function replaceCustomTemplateTags($tag, $tagContent) { - return null; - } - - // Override to initialize custom configuration settings: - function initCustomConfig() { - } - - // Override to add custom request variables: - // Add values to the request variables array: $this->_requestVars['customVariable'] = null; - function initCustomRequestVars() { - } - - // Override to add custom session code right after the session has been started: - function initCustomSession() { - } - - // Override, to parse custom info requests: - // $infoRequest contains the current info request - // Add info responses using the method addInfoMessage($info, $type) - function parseCustomInfoRequest($infoRequest) { - } - - // Override to replace custom text: - // Return replaced text - // $text contains the whole message - function replaceCustomText(&$text) { - return $text; - } - - // Override to add custom commands: - // Return true if a custom command has been successfully parsed, else false - // $text contains the whole message, $textParts the message split up as words array - function parseCustomCommands($text, $textParts) { - return false; - } - - // Override to perform custom actions on new messages: - // Return true if message may be inserted, else false - // $text contains the whole message - function onNewMessage($text) { - return true; - } - - // Override to perform custom actions on new messages: - // Method to set the style cookie depending on user data - function setStyle() { - } - - // Override: - // Returns true if the userID of the logged in user is identical to the userID of the authentication system - // or the user is authenticated as guest in the chat and the authentication system - function revalidateUserID() { - return true; - } - - // Override: - // Returns an associative array containing userName, userID and userRole - // Returns null if login is invalid - function getValidLoginUserData() { - // Check if we have a valid registered user: - if(false) { - // Here is the place to check user authentication - } else { - // Guest users: - return $this->getGuestUser(); - } - } - - // Override: - // Store the channels the current user has access to - // Make sure channel names don't contain any whitespace - function &getChannels() { - if($this->_channels === null) { - $this->_channels = $this->getAllChannels(); - } - return $this->_channels; - } - - // Override: - // Store all existing channels - // Make sure channel names don't contain any whitespace - function &getAllChannels() { - if($this->_allChannels === null) { - $this->_allChannels = array(); - - // Default channel, public to everyone: - $this->_allChannels[$this->trimChannelName($this->getConfig('defaultChannelName'))] = $this->getConfig('defaultChannelID'); - } - return $this->_allChannels; - } - -} +initialize(); + } + + function initialize() { + // Initialize configuration settings: + $this->initConfig(); + + // Initialize the DataBase connection: + $this->initDataBaseConnection(); + + // Initialize request variables: + $this->initRequestVars(); + + // Initialize the chat session: + $this->initSession(); + + // Handle the browser request and send the response content: + $this->handleRequest(); + } + + function initConfig() { + $config = null; + if (!include(AJAX_CHAT_PATH.'lib/config.php')) { + echo('Error: Could not find a config.php file in "'.AJAX_CHAT_PATH.'"lib/". Check to make sure the file exists.'); + die(); + } + $this->_config = &$config; + + // Initialize custom configuration settings: + $this->initCustomConfig(); + } + + function initRequestVars() { + $this->_requestVars = array(); + $this->_requestVars['ajax'] = isset($_REQUEST['ajax']) ? true : false; + $this->_requestVars['userID'] = isset($_REQUEST['userID']) ? (int)$_REQUEST['userID'] : null; + $this->_requestVars['userName'] = isset($_REQUEST['userName']) ? $_REQUEST['userName'] : null; + $this->_requestVars['channelID'] = isset($_REQUEST['channelID']) ? (int)$_REQUEST['channelID'] : null; + $this->_requestVars['channelName'] = isset($_REQUEST['channelName']) ? $_REQUEST['channelName'] : null; + $this->_requestVars['text'] = isset($_POST['text']) ? $_POST['text'] : null; + $this->_requestVars['lastID'] = isset($_REQUEST['lastID']) ? (int)$_REQUEST['lastID'] : 0; + $this->_requestVars['login'] = isset($_REQUEST['login']) ? true : false; + $this->_requestVars['logout'] = isset($_REQUEST['logout']) ? true : false; + $this->_requestVars['password'] = isset($_REQUEST['password']) ? $_REQUEST['password'] : null; + $this->_requestVars['view'] = isset($_REQUEST['view']) ? $_REQUEST['view'] : null; + $this->_requestVars['year'] = isset($_REQUEST['year']) ? (int)$_REQUEST['year'] : null; + $this->_requestVars['month'] = isset($_REQUEST['month']) ? (int)$_REQUEST['month'] : null; + $this->_requestVars['day'] = isset($_REQUEST['day']) ? (int)$_REQUEST['day'] : null; + $this->_requestVars['hour'] = isset($_REQUEST['hour']) ? (int)$_REQUEST['hour'] : null; + $this->_requestVars['search'] = isset($_REQUEST['search']) ? $_REQUEST['search'] : null; + $this->_requestVars['shoutbox'] = isset($_REQUEST['shoutbox']) ? true : false; + $this->_requestVars['getInfos'] = isset($_REQUEST['getInfos']) ? $_REQUEST['getInfos'] : null; + $this->_requestVars['lang'] = isset($_REQUEST['lang']) ? $_REQUEST['lang'] : null; + $this->_requestVars['delete'] = isset($_REQUEST['delete']) ? (int)$_REQUEST['delete'] : null; + + // Initialize custom request variables: + $this->initCustomRequestVars(); + + // Remove slashes which have been added to user input strings if magic_quotes_gpc is On: + if(get_magic_quotes_gpc()) { + // It is safe to remove the slashes as we escape user data ourself + array_walk( + $this->_requestVars, + create_function( + '&$value, $key', + 'if(is_string($value)) $value = stripslashes($value);' + ) + ); + } + } + + function initDataBaseConnection() { + // Create a new database object: + $this->db = new AJAXChatDataBase( + $this->_config['dbConnection'] + ); + // Use a new database connection if no existing is given: + if(!$this->_config['dbConnection']['link']) { + // Connect to the database server: + $this->db->connect($this->_config['dbConnection']); + if($this->db->error()) { + echo $this->db->getError(); + die(); + } + // Select the database: + $this->db->select($this->_config['dbConnection']['name']); + if($this->db->error()) { + echo $this->db->getError(); + die(); + } + } + // Unset the dbConnection array for safety purposes: + unset($this->_config['dbConnection']); + } + + function getDataBaseTable($table) { + return ($this->db->getName() ? '`'.$this->db->getName().'`.'.$this->getConfig('dbTableNames',$table) : $this->getConfig('dbTableNames',$table)); + } + + function initSession() { + // Start the PHP session (if not already started): + $this->startSession(); + + if($this->isLoggedIn()) { + // Logout if we receive a logout request, the chat has been closed or the userID could not be revalidated: + if($this->getRequestVar('logout') || !$this->isChatOpen() || !$this->revalidateUserID()) { + $this->logout(); + return; + } + // Logout if the Session IP is not the same when logged in and ipCheck is enabled: + if($this->getConfig('ipCheck') && ($this->getSessionIP() === null || $this->getSessionIP() != $_SERVER['REMOTE_ADDR'])) { + $this->logout('IP'); + return; + } + } else if( + // Login if auto-login enabled or a login, userName or shoutbox parameter is given: + $this->getConfig('forceAutoLogin') || + $this->getRequestVar('login') || + $this->getRequestVar('userName') || + $this->getRequestVar('shoutbox') + ) { + $this->login(); + } + + // Initialize the view: + $this->initView(); + + if($this->getView() == 'chat') { + $this->initChatViewSession(); + } else if($this->getView() == 'logs') { + $this->initLogsViewSession(); + } + + if(!$this->getRequestVar('ajax') && !headers_sent()) { + // Set style cookie: + $this->setStyle(); + // Set langCode cookie: + $this->setLangCodeCookie(); + } + + $this->initCustomSession(); + } + + function initLogsViewSession() { + if($this->getConfig('socketServerEnabled')) { + if(!$this->getSessionVar('logsViewSocketAuthenticated')) { + $this->updateLogsViewSocketAuthentication(); + $this->setSessionVar('logsViewSocketAuthenticated', true); + } + } + } + + function updateLogsViewSocketAuthentication() { + if($this->getUserRole() != AJAX_CHAT_ADMIN) { + $channels = array(); + foreach($this->getChannels() as $channel) { + if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { + continue; + } + array_push($channels, $channel); + } + array_push($channels, $this->getPrivateMessageID()); + array_push($channels, $this->getPrivateChannelID()); + } else { + // The channelID "ALL" authenticates for all channels: + $channels = array('ALL'); + } + $this->updateSocketAuthentication( + $this->getUserID(), + $this->getSocketRegistrationID(), + $channels + ); + } + + function initChatViewSession() { + // If channel is not null we are logged in to the chat view: + if($this->getChannel() !== null) { + // Check if the current user has been logged out due to inactivity: + if(!$this->isUserOnline()) { + $this->logout(); + return; + } + if($this->getRequestVar('ajax')) { + $this->initChannel(); + $this->updateOnlineStatus(); + $this->checkAndRemoveInactive(); + } + } else { + if($this->getRequestVar('ajax')) { + // Set channel, insert login messages and add to online list on first ajax request in chat view: + $this->chatViewLogin(); + } + } + } + + function isChatOpen() { + if($this->getUserRole() == AJAX_CHAT_ADMIN) + return true; + if($this->getConfig('chatClosed')) + return false; + $time = time(); + if($this->getConfig('timeZoneOffset') !== null) { + // Subtract the server timezone offset and add the config timezone offset: + $time -= date('Z', $time); + $time += $this->getConfig('timeZoneOffset'); + } + // Check the opening hours: + if($this->getConfig('openingHour') < $this->getConfig('closingHour')) + { + if(($this->getConfig('openingHour') > date('G', $time)) || ($this->getConfig('closingHour') <= date('G', $time))) + return false; + } + else + { + if(($this->getConfig('openingHour') > date('G', $time)) && ($this->getConfig('closingHour') <= date('G', $time))) + return false; + } + // Check the opening weekdays: + if(!in_array(date('w', $time), $this->getConfig('openingWeekDays'))) + return false; + return true; + } + + function handleRequest() { + if($this->getRequestVar('ajax')) { + if($this->isLoggedIn()) { + // Parse info requests (for current userName, etc.): + $this->parseInfoRequests(); + + // Parse command requests (e.g. message deletion): + $this->parseCommandRequests(); + + // Parse message requests: + $this->initMessageHandling(); + } + // Send chat messages and online user list in XML format: + $this->sendXMLMessages(); + } else { + // Display XHTML content for non-ajax requests: + $this->sendXHTMLContent(); + } + } + + function parseCommandRequests() { + if($this->getRequestVar('delete') !== null) { + $this->deleteMessage($this->getRequestVar('delete')); + } + } + + function parseInfoRequests() { + if($this->getRequestVar('getInfos')) { + $infoRequests = explode(',', $this->getRequestVar('getInfos')); + foreach($infoRequests as $infoRequest) { + $this->parseInfoRequest($infoRequest); + } + } + } + + function parseInfoRequest($infoRequest) { + switch($infoRequest) { + case 'userID': + $this->addInfoMessage($this->getUserID(), 'userID'); + break; + case 'userName': + $this->addInfoMessage($this->getUserName(), 'userName'); + break; + case 'userRole': + $this->addInfoMessage($this->getUserRole(), 'userRole'); + break; + case 'channelID': + $this->addInfoMessage($this->getChannel(), 'channelID'); + break; + case 'channelName': + $this->addInfoMessage($this->getChannelName(), 'channelName'); + break; + case 'socketRegistrationID': + $this->addInfoMessage($this->getSocketRegistrationID(), 'socketRegistrationID'); + break; + default: + $this->parseCustomInfoRequest($infoRequest); + } + } + + function sendXHTMLContent() { + $httpHeader = new AJAXChatHTTPHeader($this->getConfig('contentEncoding'), $this->getConfig('contentType')); + + $template = new AJAXChatTemplate($this, $this->getTemplateFileName(), $httpHeader->getContentType()); + + // Send HTTP header: + $httpHeader->send(); + + // Send parsed template content: + echo $template->getParsedContent(); + } + + function getTemplateFileName() { + switch($this->getView()) { + case 'chat': + return AJAX_CHAT_PATH.'lib/template/loggedIn.html'; + case 'logs': + return AJAX_CHAT_PATH.'lib/template/logs.html'; + default: + return AJAX_CHAT_PATH.'lib/template/loggedOut.html'; + } + } + + function initView() { + $this->_view = null; + // "chat" is the default view: + $view = ($this->getRequestVar('view') === null) ? 'chat' : $this->getRequestVar('view'); + if($this->hasAccessTo($view)) { + $this->_view = $view; + } + } + + function getView() { + return $this->_view; + } + + function hasAccessTo($view) { + switch($view) { + case 'chat': + case 'teaser': + if($this->isLoggedIn()) { + return true; + } + return false; + case 'logs': + if($this->isLoggedIn() && ($this->getUserRole() == AJAX_CHAT_ADMIN || + ($this->getConfig('logsUserAccess') && + ($this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == AJAX_CHAT_USER)) + )) { + return true; + } + return false; + default: + return false; + } + } + + function login() { + // Retrieve valid login user data (from request variables or session data): + $userData = $this->getValidLoginUserData(); + + if(!$userData) { + $this->addInfoMessage('errorInvalidUser'); + return false; + } + + // If the chat is closed, only the admin may login: + if(!$this->isChatOpen() && $userData['userRole'] != AJAX_CHAT_ADMIN) { + $this->addInfoMessage('errorChatClosed'); + return false; + } + + if(!$this->getConfig('allowGuestLogins') && $userData['userRole'] == AJAX_CHAT_GUEST) { + return false; + } + + // Check if userID or userName are already listed online: + if($this->isUserOnline($userData['userID']) || $this->isUserNameInUse($userData['userName'])) { + if($userData['userRole'] == AJAX_CHAT_USER || $userData['userRole'] == AJAX_CHAT_MODERATOR || $userData['userRole'] == AJAX_CHAT_ADMIN) { + // Set the registered user inactive and remove the inactive users so the user can be logged in again: + $this->setInactive($userData['userID'], $userData['userName']); + $this->removeInactive(); + } else { + $this->addInfoMessage('errorUserInUse'); + return false; + } + } + + // Check if user is banned: + if($userData['userRole'] != AJAX_CHAT_ADMIN && $this->isUserBanned($userData['userName'], $userData['userID'], $_SERVER['REMOTE_ADDR'])) { + $this->addInfoMessage('errorBanned'); + return false; + } + + // Check if the max number of users is logged in (not affecting moderators or admins): + if(!($userData['userRole'] == AJAX_CHAT_MODERATOR || $userData['userRole'] == AJAX_CHAT_ADMIN) && $this->isMaxUsersLoggedIn()) { + $this->addInfoMessage('errorMaxUsersLoggedIn'); + return false; + } + + // Use a new session id (if session has been started by the chat): + $this->regenerateSessionID(); + + // Log in: + $this->setUserID($userData['userID']); + $this->setUserName($userData['userName']); + $this->setLoginUserName($userData['userName']); + $this->setUserRole($userData['userRole']); + $this->setLoggedIn(true); + $this->setLoginTimeStamp(time()); + + // IP Security check variable: + $this->setSessionIP($_SERVER['REMOTE_ADDR']); + + // The client authenticates to the socket server using a socketRegistrationID: + if($this->getConfig('socketServerEnabled')) { + $this->setSocketRegistrationID( + md5(uniqid(rand(), true)) + ); + } + + // Add userID, userName and userRole to info messages: + $this->addInfoMessage($this->getUserID(), 'userID'); + $this->addInfoMessage($this->getUserName(), 'userName'); + $this->addInfoMessage($this->getUserRole(), 'userRole'); + + // Purge logs: + if($this->getConfig('logsPurgeLogs')) { + $this->purgeLogs(); + } + + return true; + } + + function chatViewLogin() { + $this->setChannel($this->getValidRequestChannelID()); + $this->addToOnlineList(); + + // Add channelID and channelName to info messages: + $this->addInfoMessage($this->getChannel(), 'channelID'); + $this->addInfoMessage($this->getChannelName(), 'channelName'); + + // Login message: + $text = '/login '.$this->getUserName(); + $this->insertChatBotMessage( + $this->getChannel(), + $text, + null, + 1 + ); + } + + function getValidRequestChannelID() { + $channelID = $this->getRequestVar('channelID'); + $channelName = $this->getRequestVar('channelName'); + // Check the given channelID, or get channelID from channelName: + if($channelID === null) { + if($channelName !== null) { + $channelID = $this->getChannelIDFromChannelName($channelName); + // channelName might need encoding conversion: + if($channelID === null) { + $channelID = $this->getChannelIDFromChannelName( + $this->trimChannelName($channelName, $this->getConfig('contentEncoding')) + ); + } + } + } + // Validate the resulting channelID: + if(!$this->validateChannel($channelID)) { + if($this->getChannel() !== null) { + return $this->getChannel(); + } + return $this->getConfig('defaultChannelID'); + } + return $channelID; + } + + function initChannel() { + $channelID = $this->getRequestVar('channelID'); + $channelName = $this->getRequestVar('channelName'); + if($channelID !== null) { + $this->switchChannel($this->getChannelNameFromChannelID($channelID)); + } else if($channelName !== null) { + if($this->getChannelIDFromChannelName($channelName) === null) { + // channelName might need encoding conversion: + $channelName = $this->trimChannelName($channelName, $this->getConfig('contentEncoding')); + } + $this->switchChannel($channelName); + } + } + + function logout($type=null) { + // Update the socket server authentication for the user: + if($this->getConfig('socketServerEnabled')) { + $this->updateSocketAuthentication($this->getUserID()); + } + if($this->isUserOnline()) { + $this->chatViewLogout($type); + } + $this->setLoggedIn(false); + $this->destroySession(); + + // Re-initialize the view: + $this->initView(); + } + + function chatViewLogout($type) { + $this->removeFromOnlineList(); + if($type !== null) { + $type = ' '.$type; + } + // Logout message + $text = '/logout '.$this->getUserName().$type; + $this->insertChatBotMessage( + $this->getChannel(), + $text, + null, + 1 + ); + } + + function switchChannel($channelName) { + $channelID = $this->getChannelIDFromChannelName($channelName); + + if($channelID !== null && $this->getChannel() == $channelID) { + // User is already in the given channel, return: + return; + } + + // Check if we have a valid channel: + if(!$this->validateChannel($channelID)) { + // Invalid channel: + $text = '/error InvalidChannelName '.$channelName; + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + $text + ); + return; + } + + $oldChannel = $this->getChannel(); + + $this->setChannel($channelID); + $this->updateOnlineList(); + + // Channel leave message + $text = '/channelLeave '.$this->getUserName(); + $this->insertChatBotMessage( + $oldChannel, + $text, + null, + 1 + ); + + // Channel enter message + $text = '/channelEnter '.$this->getUserName(); + $this->insertChatBotMessage( + $this->getChannel(), + $text, + null, + 1 + ); + + $this->addInfoMessage($channelName, 'channelSwitch'); + $this->addInfoMessage($channelID, 'channelID'); + $this->_requestVars['lastID'] = 0; + } + + function addToOnlineList() { + $sql = 'INSERT INTO '.$this->getDataBaseTable('online').'( + userID, + userName, + userRole, + channel, + dateTime, + ip + ) + VALUES ( + '.$this->db->makeSafe($this->getUserID()).', + '.$this->db->makeSafe($this->getUserName()).', + '.$this->db->makeSafe($this->getUserRole()).', + '.$this->db->makeSafe($this->getChannel()).', + NOW(), + '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' + );'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $this->resetOnlineUsersData(); + } + + function removeFromOnlineList() { + $sql = 'DELETE FROM + '.$this->getDataBaseTable('online').' + WHERE + userID = '.$this->db->makeSafe($this->getUserID()).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $this->removeUserFromOnlineUsersData(); + } + + function updateOnlineList() { + $sql = 'UPDATE + '.$this->getDataBaseTable('online').' + SET + userName = '.$this->db->makeSafe($this->getUserName()).', + channel = '.$this->db->makeSafe($this->getChannel()).', + dateTime = NOW(), + ip = '.$this->db->makeSafe($this->ipToStorageFormat($_SERVER['REMOTE_ADDR'])).' + WHERE + userID = '.$this->db->makeSafe($this->getUserID()).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $this->resetOnlineUsersData(); + } + + function initMessageHandling() { + // Don't handle messages if we are not in chat view: + if($this->getView() != 'chat') { + return; + } + + // Check if we have been uninvited from a private or restricted channel: + if(!$this->validateChannel($this->getChannel())) { + // Switch to the default channel: + $this->switchChannel($this->getChannelNameFromChannelID($this->getConfig('defaultChannelID'))); + return; + } + + if($this->getRequestVar('text') !== null) { + $this->insertMessage($this->getRequestVar('text')); + } + } + + function insertParsedMessage($text) { + + // If a queryUserName is set, sent all messages as private messages to this userName: + if($this->getQueryUserName() !== null && strpos($text, '/') !== 0) { + $text = '/msg '.$this->getQueryUserName().' '.$text; + } + + // Parse IRC-style commands: + if(strpos($text, '/') === 0) { + $textParts = explode(' ', $text); + + switch($textParts[0]) { + + // Channel switch: + case '/join': + $this->insertParsedMessageJoin($textParts); + break; + + // Logout: + case '/quit': + $this->logout(); + break; + + // Private message: + case '/msg': + case '/describe': + $this->insertParsedMessagePrivMsg($textParts); + break; + + // Invitation: + case '/invite': + $this->insertParsedMessageInvite($textParts); + break; + + // Uninvitation: + case '/uninvite': + $this->insertParsedMessageUninvite($textParts); + break; + + // Private messaging: + case '/query': + $this->insertParsedMessageQuery($textParts); + break; + + // Kicking offending users from the chat: + case '/kick': + $this->insertParsedMessageKick($textParts); + break; + + // Listing banned users: + case '/bans': + $this->insertParsedMessageBans($textParts); + break; + + // Unban user (remove from ban list): + case '/unban': + $this->insertParsedMessageUnban($textParts); + break; + + // Describing actions: + case '/me': + case '/action': + $this->insertParsedMessageAction($textParts); + break; + + + // Listing online Users: + case '/who': + $this->insertParsedMessageWho($textParts); + break; + + // Listing available channels: + case '/list': + $this->insertParsedMessageList($textParts); + break; + + // Retrieving the channel of a User: + case '/whereis': + $this->insertParsedMessageWhereis($textParts); + break; + + // Listing information about a User: + case '/whois': + $this->insertParsedMessageWhois($textParts); + break; + + // Rolling dice: + case '/roll': + $this->insertParsedMessageRoll($textParts); + break; + + // Switching userName: + case '/nick': + $this->insertParsedMessageNick($textParts); + break; + + // Custom or unknown command: + default: + if(!$this->parseCustomCommands($text, $textParts)) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UnknownCommand '.$textParts[0] + ); + } + } + + } else { + // No command found, just insert the plain message: + $this->insertCustomMessage( + $this->getUserID(), + $this->getUserName(), + $this->getUserRole(), + $this->getChannel(), + $text + ); + } + } + + function insertParsedMessageJoin($textParts) { + if(count($textParts) == 1) { + // join with no arguments is the own private channel, if allowed: + if($this->isAllowedToCreatePrivateChannel()) { + // Private channels are identified by square brackets: + $this->switchChannel($this->getChannelNameFromChannelID($this->getPrivateChannelID())); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingChannelName' + ); + } + } else { + $this->switchChannel($textParts[1]); + } + } + + function insertParsedMessagePrivMsg($textParts) { + if($this->isAllowedToSendPrivateMessage()) { + if(count($textParts) < 3) { + if(count($textParts) == 2) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingText' + ); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } + } else { + // Get UserID from UserName: + $toUserID = $this->getIDFromName($textParts[1]); + if($toUserID === null) { + if($this->getQueryUserName() !== null) { + // Close the current query: + $this->insertMessage('/query'); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } + } else { + // Insert /privaction command if /describe is used: + $command = ($textParts[0] == '/describe') ? '/privaction' : '/privmsg'; + // Copy of private message to current User: + $this->insertCustomMessage( + $this->getUserID(), + $this->getUserName(), + $this->getUserRole(), + $this->getPrivateMessageID(), + $command.'to '.$textParts[1].' '.implode(' ', array_slice($textParts, 2)) + ); + // Private message to requested User: + $this->insertCustomMessage( + $this->getUserID(), + $this->getUserName(), + $this->getUserRole(), + $this->getPrivateMessageID($toUserID), + $command.' '.implode(' ', array_slice($textParts, 2)) + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error PrivateMessageNotAllowed' + ); + } + } + + function insertParsedMessageInvite($textParts) { + if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + $toUserID = $this->getIDFromName($textParts[1]); + if($toUserID === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // Add the invitation to the database: + $this->addInvitation($toUserID); + $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); + // Copy of invitation to current User: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/inviteto '.$textParts[1].' '.$invitationChannelName + ); + // Invitation to requested User: + $this->insertChatBotMessage( + $this->getPrivateMessageID($toUserID), + '/invite '.$this->getUserName().' '.$invitationChannelName + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error InviteNotAllowed' + ); + } + } + + function insertParsedMessageUninvite($textParts) { + if($this->getChannel() == $this->getPrivateChannelID() || in_array($this->getChannel(), $this->getChannels())) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + $toUserID = $this->getIDFromName($textParts[1]); + if($toUserID === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // Remove the invitation from the database: + $this->removeInvitation($toUserID); + $invitationChannelName = $this->getChannelNameFromChannelID($this->getChannel()); + // Copy of uninvitation to current User: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/uninviteto '.$textParts[1].' '.$invitationChannelName + ); + // Uninvitation to requested User: + $this->insertChatBotMessage( + $this->getPrivateMessageID($toUserID), + '/uninvite '.$this->getUserName().' '.$invitationChannelName + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UninviteNotAllowed' + ); + } + } + + function insertParsedMessageQuery($textParts) { + if($this->isAllowedToSendPrivateMessage()) { + if(count($textParts) == 1) { + if($this->getQueryUserName() !== null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/queryClose '.$this->getQueryUserName() + ); + // Close the current query: + $this->setQueryUserName(null); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error NoOpenQuery' + ); + } + } else { + if($this->getIDFromName($textParts[1]) === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + if($this->getQueryUserName() !== null) { + // Close the current query: + $this->insertMessage('/query'); + } + // Open a query to the requested user: + $this->setQueryUserName($textParts[1]); + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/queryOpen '.$textParts[1] + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error PrivateMessageNotAllowed' + ); + } + } + + function insertParsedMessageKick($textParts) { + // Only moderators/admins may kick users: + if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + // Get UserID from UserName: + $kickUserID = $this->getIDFromName($textParts[1]); + if($kickUserID === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // Check the role of the user to kick: + $kickUserRole = $this->getRoleFromID($kickUserID); + if($kickUserRole == AJAX_CHAT_ADMIN || ($kickUserRole == AJAX_CHAT_MODERATOR && $this->getUserRole() != AJAX_CHAT_ADMIN)) { + // Admins and moderators may not be kicked: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error KickNotAllowed '.$textParts[1] + ); + } else { + // Kick user and insert message: + $channel = $this->getChannelFromID($kickUserID); + $banMinutes = (count($textParts) > 2) ? $textParts[2] : null; + $this->kickUser($textParts[1], $banMinutes, $kickUserID); + // If no channel found, user logged out before he could be kicked + if($channel !== null) { + $this->insertChatBotMessage( + $channel, + '/kick '.$textParts[1], + null, + 1 + ); + // Send a copy of the message to the current user, if not in the channel: + if($channel != $this->getChannel()) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/kick '.$textParts[1], + null, + 1 + ); + } + } + } + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error CommandNotAllowed '.$textParts[0] + ); + } + } + + function insertParsedMessageBans($textParts) { + // Only moderators/admins may see the list of banned users: + if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { + $this->removeExpiredBans(); + $bannedUsers = $this->getBannedUsers(); + if(count($bannedUsers) > 0) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/bans '.implode(' ', $bannedUsers) + ); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/bansEmpty -' + ); + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error CommandNotAllowed '.$textParts[0] + ); + } + } + + function insertParsedMessageUnban($textParts) { + // Only moderators/admins may unban users: + if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { + $this->removeExpiredBans(); + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + if(!in_array($textParts[1], $this->getBannedUsers())) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // Unban user and insert message: + $this->unbanUser($textParts[1]); + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/unban '.$textParts[1] + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error CommandNotAllowed '.$textParts[0] + ); + } + } + + function insertParsedMessageAction($textParts) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingText' + ); + } else { + if($this->getQueryUserName() !== null) { + // If we are in query mode, sent the action to the query user: + $this->insertMessage('/describe '.$this->getQueryUserName().' '.implode(' ', array_slice($textParts, 1))); + } else { + $this->insertCustomMessage( + $this->getUserID(), + $this->getUserName(), + $this->getUserRole(), + $this->getChannel(), + implode(' ', $textParts) + ); + } + } + } + + function insertParsedMessageWho($textParts) { + if(count($textParts) == 1) { + if($this->isAllowedToListHiddenUsers()) { + // List online users from any channel: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/who '.implode(' ', $this->getOnlineUsers()) + ); + } else { + // Get online users for all accessible channels: + $channels = $this->getChannels(); + // Add the own private channel if allowed: + if($this->isAllowedToCreatePrivateChannel()) { + array_push($channels, $this->getPrivateChannelID()); + } + // Add the invitation channels: + foreach($this->getInvitations() as $channelID) { + if(!in_array($channelID, $channels)) { + array_push($channels, $channelID); + } + } + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/who '.implode(' ', $this->getOnlineUsers($channels)) + ); + } + } else { + $channelName = $textParts[1]; + $channelID = $this->getChannelIDFromChannelName($channelName); + if(!$this->validateChannel($channelID)) { + // Invalid channel: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error InvalidChannelName '.$channelName + ); + } else { + // Get online users for the given channel: + $onlineUsers = $this->getOnlineUsers(array($channelID)); + if(count($onlineUsers) > 0) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/whoChannel '.$channelName.' '.implode(' ', $onlineUsers) + ); + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/whoEmpty -' + ); + } + } + } + } + + function insertParsedMessageList($textParts) { + // Get the names of all accessible channels: + $channelNames = $this->getChannelNames(); + // Add the own private channel, if allowed: + if($this->isAllowedToCreatePrivateChannel()) { + array_push($channelNames, $this->getPrivateChannelName()); + } + // Add the invitation channels: + foreach($this->getInvitations() as $channelID) { + $channelName = $this->getChannelNameFromChannelID($channelID); + if($channelName !== null && !in_array($channelName, $channelNames)) { + array_push($channelNames, $channelName); + } + } + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/list '.implode(' ', $channelNames) + ); + } + + function insertParsedMessageWhereis($textParts) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + // Get UserID from UserName: + $whereisUserID = $this->getIDFromName($textParts[1]); + if($whereisUserID === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + $channelID = $this->getChannelFromID($whereisUserID); + if($this->validateChannel($channelID)) { + $channelName = $this->getChannelNameFromChannelID($channelID); + } else { + $channelName = null; + } + if($channelName === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // List user information: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/whereis '.$textParts[1].' '.$channelName + ); + } + } + } + } + + function insertParsedMessageWhois($textParts) { + // Only moderators/admins: + if($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { + if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + // Get UserID from UserName: + $whoisUserID = $this->getIDFromName($textParts[1]); + if($whoisUserID === null) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameNotFound '.$textParts[1] + ); + } else { + // List user information: + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/whois '.$textParts[1].' '.$this->getIPFromID($whoisUserID) + ); + } + } + } else { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error CommandNotAllowed '.$textParts[0] + ); + } + } + + function insertParsedMessageRoll($textParts) { + if(count($textParts) == 1) { + // default is one d6: + $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); + } else { + $diceParts = explode('d', $textParts[1]); + if(count($diceParts) == 2) { + $number = (int)$diceParts[0]; + $sides = (int)$diceParts[1]; + + // Dice number must be an integer between 1 and 100, else roll only one: + $number = ($number > 0 && $number <= 100) ? $number : 1; + + // Sides must be an integer between 1 and 100, else take 6: + $sides = ($sides > 0 && $sides <= 100) ? $sides : 6; + + $text = '/roll '.$this->getUserName().' '.$number.'d'.$sides.' '; + for($i=0; $i<$number; $i++) { + if($i != 0) + $text .= ','; + $text .= $this->rollDice($sides); + } + } else { + // if dice syntax is invalid, roll one d6: + $text = '/roll '.$this->getUserName().' 1d6 '.$this->rollDice(6); + } + } + $this->insertChatBotMessage( + $this->getChannel(), + $text + ); + } + + function insertParsedMessageNick($textParts) { + if(!$this->getConfig('allowNickChange') || + (!$this->getConfig('allowGuestUserName') && $this->getUserRole() == AJAX_CHAT_GUEST)) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error CommandNotAllowed '.$textParts[0] + ); + } else if(count($textParts) == 1) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MissingUserName' + ); + } else { + $newUserName = implode(' ', array_slice($textParts, 1)); + if($newUserName == $this->getLoginUserName()) { + // Allow the user to regain the original login userName: + $prefix = ''; + $suffix = ''; + } else if($this->getUserRole() == AJAX_CHAT_GUEST) { + $prefix = $this->getConfig('guestUserPrefix'); + $suffix = $this->getConfig('guestUserSuffix'); + } else { + $prefix = $this->getConfig('changedNickPrefix'); + $suffix = $this->getConfig('changedNickSuffix'); + } + $maxLength = $this->getConfig('userNameMaxLength') + - $this->stringLength($prefix) + - $this->stringLength($suffix); + $newUserName = $this->trimString($newUserName, 'UTF-8', $maxLength, true); + if(!$newUserName) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error InvalidUserName' + ); + } else { + $newUserName = $prefix.$newUserName.$suffix; + if($this->isUserNameInUse($newUserName)) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error UserNameInUse' + ); + } else { + $oldUserName = $this->getUserName(); + $this->setUserName($newUserName); + $this->updateOnlineList(); + // Add info message to update the client-side stored userName: + $this->addInfoMessage($this->getUserName(), 'userName'); + $this->insertChatBotMessage( + $this->getChannel(), + '/nick '.$oldUserName.' '.$newUserName, + null, + 2 + ); + } + } + } + } + + function insertMessage($text) { + if(!$this->isAllowedToWriteMessage()) + return; + + if(!$this->floodControl()) + return; + + $text = $this->trimMessageText($text); + if($text == '') + return; + + if(!$this->onNewMessage($text)) + return; + + $text = $this->replaceCustomText($text); + + $this->insertParsedMessage($text); + } + + function deleteMessage($messageID) { + // Retrieve the channel of the given message: + $sql = 'SELECT + channel + FROM + '.$this->getDataBaseTable('messages').' + WHERE + id='.$this->db->makeSafe($messageID).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $row = $result->fetch(); + + if($row['channel'] !== null) { + $channel = $row['channel']; + + if($this->getUserRole() == AJAX_CHAT_ADMIN) { + $condition = ''; + } else if($this->getUserRole() == AJAX_CHAT_MODERATOR) { + $condition = ' AND + NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') + AND + NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).')'; + } else if($this->getUserRole() == AJAX_CHAT_USER && $this->getConfig('allowUserMessageDelete')) { + $condition = 'AND + ( + userID='.$this->db->makeSafe($this->getUserID()).' + OR + ( + channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' + OR + channel = '.$this->db->makeSafe($this->getPrivateChannelID()).' + ) + AND + NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_ADMIN).') + AND + NOT (userRole='.$this->db->makeSafe(AJAX_CHAT_CHATBOT).') + )'; + } else { + return false; + } + + // Remove given message from the database: + $sql = 'DELETE FROM + '.$this->getDataBaseTable('messages').' + WHERE + id='.$this->db->makeSafe($messageID).' + '.$condition.';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + if($result->affectedRows() == 1) { + // Insert a deletion command to remove the message from the clients chatlists: + $this->insertChatBotMessage($channel, '/delete '.$messageID); + return true; + } + } + return false; + } + + function floodControl() { + // Moderators and Admins need no flood control: + if($this->getUserRole() == AJAX_CHAT_MODERATOR || $this->getUserRole() == AJAX_CHAT_ADMIN) { + return true; + } + $time = time(); + // Check the time of the last inserted message: + if($this->getInsertedMessagesRateTimeStamp()+60 < $time) { + $this->setInsertedMessagesRateTimeStamp($time); + $this->setInsertedMessagesRate(1); + } else { + // Increase the inserted messages rate: + $rate = $this->getInsertedMessagesRate()+1; + $this->setInsertedMessagesRate($rate); + // Check if message rate is too high: + if($rate > $this->getConfig('maxMessageRate')) { + $this->insertChatBotMessage( + $this->getPrivateMessageID(), + '/error MaxMessageRate' + ); + // Return false so the message is not inserted: + return false; + } + } + return true; + } + + function isAllowedToWriteMessage() { + if($this->getUserRole() != AJAX_CHAT_GUEST) + return true; + if($this->getConfig('allowGuestWrite')) + return true; + return false; + } + + function insertChatBotMessage($channelID, $messageText, $ip=null, $mode=0) { + $this->insertCustomMessage( + $this->getConfig('chatBotID'), + $this->getConfig('chatBotName'), + AJAX_CHAT_CHATBOT, + $channelID, + $messageText, + $ip, + $mode + ); + } + + function insertCustomMessage($userID, $userName, $userRole, $channelID, $text, $ip=null, $mode=0) { + // The $mode parameter is used for socket updates: + // 0 = normal messages + // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) + // 2 = messages with online user updates (nick) + + $ip = $ip ? $ip : $_SERVER['REMOTE_ADDR']; + + $sql = 'INSERT INTO '.$this->getDataBaseTable('messages').'( + userID, + userName, + userRole, + channel, + dateTime, + ip, + text + ) + VALUES ( + '.$this->db->makeSafe($userID).', + '.$this->db->makeSafe($userName).', + '.$this->db->makeSafe($userRole).', + '.$this->db->makeSafe($channelID).', + NOW(), + '.$this->db->makeSafe($this->ipToStorageFormat($ip)).', + '.$this->db->makeSafe($text).' + );'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + if($this->getConfig('socketServerEnabled')) { + $this->sendSocketMessage( + $this->getSocketBroadcastMessage( + $this->db->getLastInsertedID(), + time(), + $userID, + $userName, + $userRole, + $channelID, + $text, + $mode + ) + ); + } + } + + function getSocketBroadcastMessage( + $messageID, + $timeStamp, + $userID, + $userName, + $userRole, + $channelID, + $text, + $mode + ) { + // The $mode parameter: + // 0 = normal messages + // 1 = channel messages (e.g. login/logout, channel enter/leave, kick) + // 2 = messages with online user updates (nick) + + // Get the message XML content: + $xml = ''; + if($mode) { + // Add the list of online users if the user list has been updated ($mode > 0): + $xml .= $this->getChatViewOnlineUsersXML(array($channelID)); + } + if($mode != 1 || $this->getConfig('showChannelMessages')) { + $xml .= ''; + $xml .= $this->getChatViewMessageXML( + $messageID, + $timeStamp, + $userID, + $userName, + $userRole, + $channelID, + $text + ); + $xml .= ''; + } + $xml .= ''; + return $xml; + } + + function sendSocketMessage($message) { + // Open a TCP socket connection to the socket server: + if($socket = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) { + if(@socket_connect($socket, $this->getConfig('socketServerIP'), $this->getConfig('socketServerPort'))) { + // Append a null-byte to the string as EOL (End Of Line) character + // which is required by Flash XML socket communication: + $message .= "\0"; + @socket_write( + $socket, + $message, + strlen($message) // Using strlen to count the bytes instead of the number of UTF-8 characters + ); + } + @socket_close($socket); + } + } + + function updateSocketAuthentication($userID, $socketRegistrationID=null, $channels=null) { + // If no $socketRegistrationID or no $channels are given the authentication is removed for the given user: + $authentication = ''; + if($channels) { + foreach($channels as $channelID) { + $authentication .= ''; + } + } + $authentication .= ''; + $this->sendSocketMessage($authentication); + } + + function setSocketRegistrationID($value) { + $this->setSessionVar('SocketRegistrationID', $value); + } + + function getSocketRegistrationID() { + return $this->getSessionVar('SocketRegistrationID'); + } + + function rollDice($sides) { + // seed with microseconds since last "whole" second: + mt_srand((double)microtime()*1000000); + + return mt_rand(1, $sides); + } + + function kickUser($userName, $banMinutes=null, $userID=null) { + if($userID === null) { + $userID = $this->getIDFromName($userName); + } + if($userID === null) { + return; + } + + $banMinutes = ($banMinutes !== null) ? $banMinutes : $this->getConfig('defaultBanTime'); + + if($banMinutes) { + // Ban User for the given time in minutes: + $this->banUser($userName, $banMinutes, $userID); + } + + // Remove given User from online list: + $sql = 'DELETE FROM + '.$this->getDataBaseTable('online').' + WHERE + userID = '.$this->db->makeSafe($userID).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + // Update the socket server authentication for the kicked user: + if($this->getConfig('socketServerEnabled')) { + $this->updateSocketAuthentication($userID); + } + + $this->removeUserFromOnlineUsersData($userID); + } + + function getBannedUsersData($key=null, $value=null) { + if($this->_bannedUsersData === null) { + $this->_bannedUsersData = array(); + + $sql = 'SELECT + userID, + userName, + ip + FROM + '.$this->getDataBaseTable('bans').' + WHERE + NOW() < dateTime;'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + while($row = $result->fetch()) { + $row['ip'] = $this->ipFromStorageFormat($row['ip']); + array_push($this->_bannedUsersData, $row); + } + + $result->free(); + } + + if($key) { + $bannedUsersData = array(); + foreach($this->_bannedUsersData as $bannedUserData) { + if(!isset($bannedUserData[$key])) { + return $bannedUsersData; + } + if($value) { + if($bannedUserData[$key] == $value) { + array_push($bannedUsersData, $bannedUserData); + } else { + continue; + } + } else { + array_push($bannedUsersData, $bannedUserData[$key]); + } + } + return $bannedUsersData; + } + + return $this->_bannedUsersData; + } + + function getBannedUsers() { + return $this->getBannedUsersData('userName'); + } + + function banUser($userName, $banMinutes=null, $userID=null) { + if($userID === null) { + $userID = $this->getIDFromName($userName); + } + $ip = $this->getIPFromID($userID); + if(!$ip || $userID === null) { + return; + } + + // Remove expired bans: + $this->removeExpiredBans(); + + $banMinutes = (int)$banMinutes; + if(!$banMinutes) { + // If banMinutes is not a valid integer, use the defaultBanTime: + $banMinutes = $this->getConfig('defaultBanTime'); + } + + $sql = 'INSERT INTO '.$this->getDataBaseTable('bans').'( + userID, + userName, + dateTime, + ip + ) + VALUES ( + '.$this->db->makeSafe($userID).', + '.$this->db->makeSafe($userName).', + DATE_ADD(NOW(), interval '.$this->db->makeSafe($banMinutes).' MINUTE), + '.$this->db->makeSafe($this->ipToStorageFormat($ip)).' + );'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function unbanUser($userName) { + $sql = 'DELETE FROM + '.$this->getDataBaseTable('bans').' + WHERE + userName = '.$this->db->makeSafe($userName).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function removeExpiredBans() { + $sql = 'DELETE FROM + '.$this->getDataBaseTable('bans').' + WHERE + dateTime < NOW();'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function setInactive($userID, $userName=null) { + $condition = 'userID='.$this->db->makeSafe($userID); + if($userName !== null) { + $condition .= ' OR userName='.$this->db->makeSafe($userName); + } + $sql = 'UPDATE + '.$this->getDataBaseTable('online').' + SET + dateTime = DATE_SUB(NOW(), interval '.(intval($this->getConfig('inactiveTimeout'))+1).' MINUTE) + WHERE + '.$condition.';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $this->resetOnlineUsersData(); + } + + function removeInactive() { + $sql = 'SELECT + userID, + userName, + channel + FROM + '.$this->getDataBaseTable('online').' + WHERE + NOW() > DATE_ADD(dateTime, interval '.$this->getConfig('inactiveTimeout').' MINUTE);'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + if($result->numRows() > 0) { + $condition = ''; + while($row = $result->fetch()) { + if(!empty($condition)) + $condition .= ' OR '; + // Add userID to condition for removal: + $condition .= 'userID='.$this->db->makeSafe($row['userID']); + + // Update the socket server authentication for the kicked user: + if($this->getConfig('socketServerEnabled')) { + $this->updateSocketAuthentication($row['userID']); + } + + $this->removeUserFromOnlineUsersData($row['userID']); + + // Insert logout timeout message: + $text = '/logout '.$row['userName'].' Timeout'; + $this->insertChatBotMessage( + $row['channel'], + $text, + null, + 1 + ); + } + + $result->free(); + + $sql = 'DELETE FROM + '.$this->getDataBaseTable('online').' + WHERE + '.$condition.';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + } + + function updateOnlineStatus() { + // Update online status every 50 seconds (this allows update requests to be in time): + if(!$this->getStatusUpdateTimeStamp() || ((time() - $this->getStatusUpdateTimeStamp()) > 50)) { + $this->updateOnlineList(); + $this->setStatusUpdateTimeStamp(time()); + } + } + + function checkAndRemoveInactive() { + // Remove inactive users every inactiveCheckInterval: + if(!$this->getInactiveCheckTimeStamp() || ((time() - $this->getInactiveCheckTimeStamp()) > $this->getConfig('inactiveCheckInterval')*60)) { + $this->removeInactive(); + $this->setInactiveCheckTimeStamp(time()); + } + } + + function sendXMLMessages() { + $httpHeader = new AJAXChatHTTPHeader('UTF-8', 'text/xml'); + + // Send HTTP header: + $httpHeader->send(); + + // Output XML messages: + echo $this->getXMLMessages(); + } + + function getXMLMessages() { + switch($this->getView()) { + case 'chat': + return $this->getChatViewXMLMessages(); + case 'teaser': + return $this->getTeaserViewXMLMessages(); + case 'logs': + return $this->getLogsViewXMLMessages(); + default: + return $this->getLogoutXMLMessage(); + } + } + + function getMessageCondition() { + $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')).' + AND ( + channel = '.$this->db->makeSafe($this->getChannel()).' + OR + channel = '.$this->db->makeSafe($this->getPrivateMessageID()).' + ) + AND + '; + if($this->getConfig('requestMessagesPriorChannelEnter') || + ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($this->getChannel(), $this->getConfig('requestMessagesPriorChannelEnterList')))) { + $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; + } else { + $condition .= 'dateTime >= FROM_UNIXTIME(' . $this->getChannelEnterTimeStamp() . ')'; + } + return $condition; + } + + function getMessageFilter() { + $filterChannelMessages = ''; + if(!$this->getConfig('showChannelMessages') || $this->getRequestVar('shoutbox')) { + $filterChannelMessages = ' AND NOT ( + text LIKE (\'/login%\') + OR + text LIKE (\'/logout%\') + OR + text LIKE (\'/channelEnter%\') + OR + text LIKE (\'/channelLeave%\') + OR + text LIKE (\'/kick%\') + )'; + } + return $filterChannelMessages; + } + + function getInfoMessagesXML() { + $xml = ''; + // Go through the info messages: + foreach($this->getInfoMessages() as $type=>$infoArray) { + foreach($infoArray as $info) { + $xml .= ''; + $xml .= 'encodeSpecialChars($info).']]>'; + $xml .= ''; + } + } + $xml .= ''; + return $xml; + } + + function getChatViewOnlineUsersXML($channelIDs) { + // Get the online users for the given channels: + $onlineUsersData = $this->getOnlineUsersData($channelIDs); + $xml = ''; + foreach($onlineUsersData as $onlineUserData) { + $xml .= 'encodeSpecialChars($onlineUserData['userName']).']]>'; + $xml .= ''; + } + $xml .= ''; + return $xml; + } + + function getLogoutXMLMessage() { + $xml = ''; + $xml .= ''; + $xml .= ''; + $xml .= ''; + $xml .= 'encodeSpecialChars($this->getConfig('logoutData')).']]>'; + $xml .= ''; + $xml .= ''; + $xml .= ''; + return $xml; + } + + function getChatViewMessageXML( + $messageID, + $timeStamp, + $userID, + $userName, + $userRole, + $channelID, + $text + ) { + $message = 'encodeSpecialChars($userName).']]>'; + $message .= 'encodeSpecialChars($text).']]>'; + $message .= ''; + return $message; + } + + function getChatViewMessagesXML() { + // Get the last messages in descending order (this optimises the LIMIT usage): + $sql = 'SELECT + id, + userID, + userName, + userRole, + channel AS channelID, + UNIX_TIMESTAMP(dateTime) AS timeStamp, + text + FROM + '.$this->getDataBaseTable('messages').' + WHERE + '.$this->getMessageCondition().' + '.$this->getMessageFilter().' + ORDER BY + id + DESC + LIMIT '.$this->getConfig('requestMessagesLimit').';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $messages = ''; + + // Add the messages in reverse order so it is ascending again: + while($row = $result->fetch()) { + $message = $this->getChatViewMessageXML( + $row['id'], + $row['timeStamp'], + $row['userID'], + $row['userName'], + $row['userRole'], + $row['channelID'], + $row['text'] + ); + $messages = $message.$messages; + } + $result->free(); + + $messages = ''.$messages.''; + return $messages; + } + + function getChatViewXMLMessages() { + $xml = ''; + $xml .= ''; + $xml .= $this->getInfoMessagesXML(); + $xml .= $this->getChatViewOnlineUsersXML(array($this->getChannel())); + $xml .= $this->getChatViewMessagesXML(); + $xml .= ''; + return $xml; + } + + function getTeaserMessageCondition() { + $channelID = $this->getValidRequestChannelID(); + $condition = 'channel = '.$this->db->makeSafe($channelID).' + AND + '; + if($this->getConfig('requestMessagesPriorChannelEnter') || + ($this->getConfig('requestMessagesPriorChannelEnterList') && in_array($channelID, $this->getConfig('requestMessagesPriorChannelEnterList')))) { + $condition .= 'NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('requestMessagesTimeDiff').' HOUR)'; + } else { + // Teaser content may not be shown for this channel: + $condition .= '0 = 1'; + } + return $condition; + } + + function getTeaserViewMessagesXML() { + // Get the last messages in descending order (this optimises the LIMIT usage): + $sql = 'SELECT + id, + userID, + userName, + userRole, + channel AS channelID, + UNIX_TIMESTAMP(dateTime) AS timeStamp, + text + FROM + '.$this->getDataBaseTable('messages').' + WHERE + '.$this->getTeaserMessageCondition().' + '.$this->getMessageFilter().' + ORDER BY + id + DESC + LIMIT '.$this->getConfig('requestMessagesLimit').';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $messages = ''; + + // Add the messages in reverse order so it is ascending again: + while($row = $result->fetch()) { + $message = ''; + $message .= 'encodeSpecialChars($row['userName']).']]>'; + $message .= 'encodeSpecialChars($row['text']).']]>'; + $message .= ''; + $messages = $message.$messages; + } + $result->free(); + + $messages = ''.$messages.''; + return $messages; + } + + function getTeaserViewXMLMessages() { + $xml = ''; + $xml .= ''; + $xml .= $this->getInfoMessagesXML(); + $xml .= $this->getTeaserViewMessagesXML(); + $xml .= ''; + return $xml; + } + + function getLogsViewCondition() { + $condition = 'id > '.$this->db->makeSafe($this->getRequestVar('lastID')); + + // Check the channel condition: + switch($this->getRequestVar('channelID')) { + case '-3': + // Just display messages from all accessible channels + if($this->getUserRole() != AJAX_CHAT_ADMIN) { + $condition .= ' AND (channel = '.$this->db->makeSafe($this->getPrivateMessageID()); + $condition .= ' OR channel = '.$this->db->makeSafe($this->getPrivateChannelID()); + foreach($this->getChannels() as $channel) { + if($this->getConfig('logsUserAccessChannelList') && !in_array($channel, $this->getConfig('logsUserAccessChannelList'))) { + continue; + } + $condition .= ' OR channel = '.$this->db->makeSafe($channel); + } + $condition .= ')'; + } + break; + case '-2': + if($this->getUserRole() != AJAX_CHAT_ADMIN) { + $condition .= ' AND channel = '.($this->getPrivateMessageID()); + } else { + $condition .= ' AND channel > '.($this->getConfig('privateMessageDiff')-1); + } + break; + case '-1': + if($this->getUserRole() != AJAX_CHAT_ADMIN) { + $condition .= ' AND channel = '.($this->getPrivateChannelID()); + } else { + $condition .= ' AND (channel > '.($this->getConfig('privateChannelDiff')-1).' AND channel < '.($this->getConfig('privateMessageDiff')).')'; + } + break; + default: + if(($this->getUserRole() == AJAX_CHAT_ADMIN || !$this->getConfig('logsUserAccessChannelList') || in_array($this->getRequestVar('channelID'), $this->getConfig('logsUserAccessChannelList'))) + && $this->validateChannel($this->getRequestVar('channelID'))) { + $condition .= ' AND channel = '.$this->db->makeSafe($this->getRequestVar('channelID')); + } else { + // No valid channel: + $condition .= ' AND 0 = 1'; + } + } + + // Check the period condition: + $hour = ($this->getRequestVar('hour') === null || $this->getRequestVar('hour') > 23 || $this->getRequestVar('hour') < 0) ? null : $this->getRequestVar('hour'); + $day = ($this->getRequestVar('day') === null || $this->getRequestVar('day') > 31 || $this->getRequestVar('day') < 1) ? null : $this->getRequestVar('day'); + $month = ($this->getRequestVar('month') === null || $this->getRequestVar('month') > 12 || $this->getRequestVar('month') < 1) ? null : $this->getRequestVar('month'); + $year = ($this->getRequestVar('year') === null || $this->getRequestVar('year') > date('Y') || $this->getRequestVar('year') < $this->getConfig('logsFirstYear')) ? null : $this->getRequestVar('year'); + + // If a time (hour) is given but no date (year, month, day), use the current date: + if($hour !== null) { + if($day === null) + $day = date('j'); + if($month === null) + $month = date('n'); + if($year === null) + $year = date('Y'); + } + + if($year === null) { + // No year given, so no period condition + } else if($month === null) { + // Define the given year as period: + $periodStart = mktime(0, 0, 0, 1, 1, $year); + // The last day in a month can be expressed by using 0 for the day of the next month: + $periodEnd = mktime(23, 59, 59, 13, 0, $year); + } else if($day === null) { + // Define the given month as period: + $periodStart = mktime(0, 0, 0, $month, 1, $year); + // The last day in a month can be expressed by using 0 for the day of the next month: + $periodEnd = mktime(23, 59, 59, $month+1, 0, $year); + } else if($hour === null){ + // Define the given day as period: + $periodStart = mktime(0, 0, 0, $month, $day, $year); + $periodEnd = mktime(23, 59, 59, $month, $day, $year); + } else { + // Define the given hour as period: + $periodStart = mktime($hour, 0, 0, $month, $day, $year); + $periodEnd = mktime($hour, 59, 59, $month, $day, $year); + } + + if(isset($periodStart)) + $condition .= ' AND dateTime > \''.date('Y-m-d H:i:s', $periodStart).'\' AND dateTime <= \''.date('Y-m-d H:i:s', $periodEnd).'\''; + + // Check the search condition: + if($this->getRequestVar('search')) { + if(($this->getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) && strpos($this->getRequestVar('search'), 'ip=') === 0) { + // Search for messages with the given IP: + $ip = substr($this->getRequestVar('search'), 3); + $condition .= ' AND (ip = '.$this->db->makeSafe($this->ipToStorageFormat($ip)).')'; + } else if(strpos($this->getRequestVar('search'), 'userID=') === 0) { + // Search for messages with the given userID: + $userID = substr($this->getRequestVar('search'), 7); + $condition .= ' AND (userID = '.$this->db->makeSafe($userID).')'; + } else { + // Use the search value as regular expression on message text and username: + $condition .= ' AND (userName REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).' OR text REGEXP '.$this->db->makeSafe($this->getRequestVar('search')).')'; + } + } + + // If no period or search condition is given, just monitor the last messages on the given channel: + if(!isset($periodStart) && !$this->getRequestVar('search')) { + $condition .= ' AND NOW() < DATE_ADD(dateTime, interval '.$this->getConfig('logsRequestMessagesTimeDiff').' HOUR)'; + } + + return $condition; + } + + function getLogsViewMessagesXML() { + $sql = 'SELECT + id, + userID, + userName, + userRole, + channel AS channelID, + UNIX_TIMESTAMP(dateTime) AS timeStamp, + ip, + text + FROM + '.$this->getDataBaseTable('messages').' + WHERE + '.$this->getLogsViewCondition().' + ORDER BY + id + LIMIT '.$this->getConfig('logsRequestMessagesLimit').';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + $xml = ''; + while($row = $result->fetch()) { + $xml .= 'getUserRole() == AJAX_CHAT_ADMIN || $this->getUserRole() == AJAX_CHAT_MODERATOR) { + $xml .= ' ip="'.$this->ipFromStorageFormat($row['ip']).'"'; + } + $xml .= '>'; + $xml .= 'encodeSpecialChars($row['userName']).']]>'; + $xml .= 'encodeSpecialChars($row['text']).']]>'; + $xml .= ''; + } + $result->free(); + + $xml .= ''; + + return $xml; + } + + function getLogsViewXMLMessages() { + $xml = ''; + $xml .= ''; + $xml .= $this->getInfoMessagesXML(); + $xml .= $this->getLogsViewMessagesXML(); + $xml .= ''; + return $xml; + } + + function purgeLogs() { + $sql = 'DELETE FROM + '.$this->getDataBaseTable('messages').' + WHERE + dateTime < DATE_SUB(NOW(), interval '.$this->getConfig('logsPurgeTimeDiff').' DAY);'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function getInfoMessages($type=null) { + if(!isset($this->_infoMessages)) { + $this->_infoMessages = array(); + } + if($type) { + if(!isset($this->_infoMessages[$type])) { + $this->_infoMessages[$type] = array(); + } + return $this->_infoMessages[$type]; + } else { + return $this->_infoMessages; + } + } + + function addInfoMessage($info, $type='error') { + if(!isset($this->_infoMessages)) { + $this->_infoMessages = array(); + } + if(!isset($this->_infoMessages[$type])) { + $this->_infoMessages[$type] = array(); + } + if(!in_array($info, $this->_infoMessages[$type])) { + array_push($this->_infoMessages[$type], $info); + } + } + + function getRequestVars() { + return $this->_requestVars; + } + + function getRequestVar($key) { + if($this->_requestVars && isset($this->_requestVars[$key])) { + return $this->_requestVars[$key]; + } + return null; + } + + function setRequestVar($key, $value) { + if(!$this->_requestVars) { + $this->_requestVars = array(); + } + $this->_requestVars[$key] = $value; + } + + function getOnlineUsersData($channelIDs=null, $key=null, $value=null) { + if($this->_onlineUsersData === null) { + $this->_onlineUsersData = array(); + + $sql = 'SELECT + userID, + userName, + userRole, + channel, + UNIX_TIMESTAMP(dateTime) AS timeStamp, + ip + FROM + '.$this->getDataBaseTable('online').' + ORDER BY + userName;'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + while($row = $result->fetch()) { + $row['ip'] = $this->ipFromStorageFormat($row['ip']); + array_push($this->_onlineUsersData, $row); + } + + $result->free(); + } + + if($channelIDs || $key) { + $onlineUsersData = array(); + foreach($this->_onlineUsersData as $userData) { + if($channelIDs && !in_array($userData['channel'], $channelIDs)) { + continue; + } + if($key) { + if(!isset($userData[$key])) { + return $onlineUsersData; + } + if($value !== null) { + if($userData[$key] == $value) { + array_push($onlineUsersData, $userData); + } else { + continue; + } + } else { + array_push($onlineUsersData, $userData[$key]); + } + } else { + array_push($onlineUsersData, $userData); + } + } + return $onlineUsersData; + } + + return $this->_onlineUsersData; + } + + function removeUserFromOnlineUsersData($userID=null) { + if(!$this->_onlineUsersData) { + return; + } + $userID = ($userID === null) ? $this->getUserID() : $userID; + for($i=0; $i_onlineUsersData); $i++) { + if($this->_onlineUsersData[$i]['userID'] == $userID) { + array_splice($this->_onlineUsersData, $i, 1); + break; + } + } + } + + function resetOnlineUsersData() { + $this->_onlineUsersData = null; + } + + function getOnlineUsers($channelIDs=null) { + return $this->getOnlineUsersData($channelIDs, 'userName'); + } + + function getOnlineUserIDs($channelIDs=null) { + return $this->getOnlineUsersData($channelIDs, 'userID'); + } + + function startSession() { + if(!session_id()) { + // Set the session name: + session_name($this->getConfig('sessionName')); + + // Set session cookie parameters: + session_set_cookie_params( + 0, // The session is destroyed on logout anyway, so no use to set this + $this->getConfig('sessionCookiePath'), + $this->getConfig('sessionCookieDomain'), + $this->getConfig('sessionCookieSecure') + ); + + // Start the session: + session_start(); + + // We started a new session: + $this->_sessionNew = true; + } + } + + function destroySession() { + if($this->_sessionNew) { + // Delete all session variables: + $_SESSION = array(); + + // Delete the session cookie: + if (isset($_COOKIE[session_name()])) { + setcookie( + session_name(), + '', + time()-42000, + $this->getConfig('sessionCookiePath'), + $this->getConfig('sessionCookieDomain'), + $this->getConfig('sessionCookieSecure') + ); + } + + // Destroy the session: + session_destroy(); + } else { + // Unset all session variables starting with the sessionKeyPrefix: + foreach($_SESSION as $key=>$value) { + if(strpos($key, $this->getConfig('sessionKeyPrefix')) === 0) { + unset($_SESSION[$key]); + } + } + } + } + + function regenerateSessionID() { + if($this->_sessionNew) { + // Regenerate session id: + @session_regenerate_id(true); + } + } + + function getSessionVar($key, $prefix=null) { + if($prefix === null) + $prefix = $this->getConfig('sessionKeyPrefix'); + + // Return the session value if existing: + if(isset($_SESSION[$prefix.$key])) + return $_SESSION[$prefix.$key]; + else + return null; + } + + function setSessionVar($key, $value, $prefix=null) { + if($prefix === null) + $prefix = $this->getConfig('sessionKeyPrefix'); + + // Set the session value: + $_SESSION[$prefix.$key] = $value; + } + + function getSessionIP() { + return $this->getSessionVar('IP'); + } + + function setSessionIP($ip) { + $this->setSessionVar('IP', $ip); + } + + function getQueryUserName() { + return $this->getSessionVar('QueryUserName'); + } + + function setQueryUserName($userName) { + $this->setSessionVar('QueryUserName', $userName); + } + + function getInvitations() { + if($this->_invitations === null) { + $this->_invitations = array(); + + $sql = 'SELECT + channel + FROM + '.$this->getDataBaseTable('invitations').' + WHERE + userID='.$this->db->makeSafe($this->getUserID()).' + AND + DATE_SUB(NOW(), interval 1 DAY) < dateTime;'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + + while($row = $result->fetch()) { + array_push($this->_invitations, $row['channel']); + } + + $result->free(); + } + return $this->_invitations; + } + + function removeExpiredInvitations() { + $sql = 'DELETE FROM + '.$this->getDataBaseTable('invitations').' + WHERE + DATE_SUB(NOW(), interval 1 DAY) > dateTime;'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function addInvitation($userID, $channelID=null) { + $this->removeExpiredInvitations(); + + $channelID = ($channelID === null) ? $this->getChannel() : $channelID; + + $sql = 'INSERT INTO '.$this->getDataBaseTable('invitations').'( + userID, + channel, + dateTime + ) + VALUES ( + '.$this->db->makeSafe($userID).', + '.$this->db->makeSafe($channelID).', + NOW() + );'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function removeInvitation($userID, $channelID=null) { + $channelID = ($channelID === null) ? $this->getChannel() : $channelID; + + $sql = 'DELETE FROM + '.$this->getDataBaseTable('invitations').' + WHERE + userID='.$this->db->makeSafe($userID).' + AND + channel='.$this->db->makeSafe($channelID).';'; + + // Create a new SQL query: + $result = $this->db->sqlQuery($sql); + + // Stop if an error occurs: + if($result->error()) { + echo $result->getError(); + die(); + } + } + + function getUserID() { + return $this->getSessionVar('UserID'); + } + + function setUserID($id) { + $this->setSessionVar('UserID', $id); + } + + function getUserName() { + return $this->getSessionVar('UserName'); + } + + function setUserName($name) { + $this->setSessionVar('UserName', $name); + } + + function getLoginUserName() { + return $this->getSessionVar('LoginUserName'); + } + + function setLoginUserName($name) { + $this->setSessionVar('LoginUserName', $name); + } + + function getUserRole() { + $userRole = $this->getSessionVar('UserRole'); + if($userRole === null) + return AJAX_CHAT_GUEST; + return $userRole; + } + + function setUserRole($role) { + $this->setSessionVar('UserRole', $role); + } + + function getChannel() { + return $this->getSessionVar('Channel'); + } + + function setChannel($channel) { + $this->setSessionVar('Channel', $channel); + + // Save the channel enter timestamp: + $this->setChannelEnterTimeStamp(time()); + + // Update the channel authentication for the socket server: + if($this->getConfig('socketServerEnabled')) { + $this->updateSocketAuthentication( + $this->getUserID(), + $this->getSocketRegistrationID(), + array($channel,$this->getPrivateMessageID()) + ); + } + + // Reset the logs view socket authentication session var: + if($this->getSessionVar('logsViewSocketAuthenticated')) { + $this->setSessionVar('logsViewSocketAuthenticated', false); + } + } + + function isLoggedIn() { + return (bool)$this->getSessionVar('LoggedIn'); + } + + function setLoggedIn($bool) { + $this->setSessionVar('LoggedIn', $bool); + } + + function getLoginTimeStamp() { + return $this->getSessionVar('LoginTimeStamp'); + } + + function setLoginTimeStamp($time) { + $this->setSessionVar('LoginTimeStamp', $time); + } + + function getChannelEnterTimeStamp() { + return $this->getSessionVar('ChannelEnterTimeStamp'); + } + + function setChannelEnterTimeStamp($time) { + $this->setSessionVar('ChannelEnterTimeStamp', $time); + } + + function getStatusUpdateTimeStamp() { + return $this->getSessionVar('StatusUpdateTimeStamp'); + } + + function setStatusUpdateTimeStamp($time) { + $this->setSessionVar('StatusUpdateTimeStamp', $time); + } + + function getInactiveCheckTimeStamp() { + return $this->getSessionVar('InactiveCheckTimeStamp'); + } + + function setInactiveCheckTimeStamp($time) { + $this->setSessionVar('InactiveCheckTimeStamp', $time); + } + + function getInsertedMessagesRate() { + return $this->getSessionVar('InsertedMessagesRate'); + } + + function setInsertedMessagesRate($rate) { + $this->setSessionVar('InsertedMessagesRate', $rate); + } + + function getInsertedMessagesRateTimeStamp() { + return $this->getSessionVar('InsertedMessagesRateTimeStamp'); + } + + function setInsertedMessagesRateTimeStamp($time) { + $this->setSessionVar('InsertedMessagesRateTimeStamp', $time); + } + + function getLangCode() { + // Get the langCode from request or cookie: + $langCodeCookie = isset($_COOKIE[$this->getConfig('sessionName').'_lang']) ? $_COOKIE[$this->getConfig('sessionName').'_lang'] : null; + $langCode = $this->getRequestVar('lang') ? $this->getRequestVar('lang') : $langCodeCookie; + // Check if the langCode is valid: + if(!in_array($langCode, $this->getConfig('langAvailable'))) { + // Determine the user language: + $language = new AJAXChatLanguage($this->getConfig('langAvailable'), $this->getConfig('langDefault')); + $langCode = $language->getLangCode(); + } + return $langCode; + } + + function setLangCodeCookie() { + setcookie( + $this->getConfig('sessionName').'_lang', + $this->getLangCode(), + time()+60*60*24*$this->getConfig('sessionCookieLifeTime'), + $this->getConfig('sessionCookiePath'), + $this->getConfig('sessionCookieDomain'), + $this->getConfig('sessionCookieSecure') + ); + } + + function removeUnsafeCharacters($str) { + // Remove NO-WS-CTL, non-whitespace control characters (RFC 2822), decimal 1–8, 11–12, 14–31, and 127: + return AJAXChatEncoding::removeUnsafeCharacters($str); + } + + function subString($str, $start=0, $length=null, $encoding='UTF-8') { + return AJAXChatString::subString($str, $start, $length, $encoding); + } + + function stringLength($str, $encoding='UTF-8') { + return AJAXChatString::stringLength($str, $encoding); + } + + function trimMessageText($text) { + return $this->trimString($text, 'UTF-8', $this->getConfig('messageTextMaxLength')); + } + + function trimUserName($userName) { + return $this->trimString($userName, null, $this->getConfig('userNameMaxLength'), true, true); + } + + function trimChannelName($channelName) { + return $this->trimString($channelName, null, null, true, true); + } + + function trimString($str, $sourceEncoding=null, $maxLength=null, $replaceWhitespace=false, $decodeEntities=false, $htmlEntitiesMap=null) { + // Make sure the string contains valid unicode: + $str = $this->convertToUnicode($str, $sourceEncoding); + + // Make sure the string contains no unsafe characters: + $str = $this->removeUnsafeCharacters($str); + + // Strip whitespace from the beginning and end of the string: + $str = trim($str); + + if($replaceWhitespace) { + // Replace any whitespace in the userName with the underscore "_": + $str = preg_replace('/\s/u', '_', $str); + } + + if($decodeEntities) { + // Decode entities: + $str = $this->decodeEntities($str, 'UTF-8', $htmlEntitiesMap); + } + + if($maxLength) { + // Cut the string to the allowed length: + $str = $this->subString($str, 0, $maxLength); + } + + return $str; + } + + function convertToUnicode($str, $sourceEncoding=null) { + if($sourceEncoding === null) { + $sourceEncoding = $this->getConfig('sourceEncoding'); + } + return $this->convertEncoding($str, $sourceEncoding, 'UTF-8'); + } + + function convertFromUnicode($str, $contentEncoding=null) { + if($contentEncoding === null) { + $contentEncoding = $this->getConfig('contentEncoding'); + } + return $this->convertEncoding($str, 'UTF-8', $contentEncoding); + } + + function convertEncoding($str, $charsetFrom, $charsetTo) { + return AJAXChatEncoding::convertEncoding($str, $charsetFrom, $charsetTo); + } + + function encodeEntities($str, $encoding='UTF-8', $convmap=null) { + return AJAXChatEncoding::encodeEntities($str, $encoding, $convmap); + } + + function decodeEntities($str, $encoding='UTF-8', $htmlEntitiesMap=null) { + return AJAXChatEncoding::decodeEntities($str, $encoding, $htmlEntitiesMap); + } + + function htmlEncode($str) { + return AJAXChatEncoding::htmlEncode($str, $this->getConfig('contentEncoding')); + } + + function encodeSpecialChars($str) { + return AJAXChatEncoding::encodeSpecialChars($str); + } + + function decodeSpecialChars($str) { + return AJAXChatEncoding::decodeSpecialChars($str); + } + + function ipToStorageFormat($ip) { + if(function_exists('inet_pton')) { + // ipv4 & ipv6: + return @inet_pton($ip); + } + // Only ipv4: + return @pack('N',@ip2long($ip)); + } + + function ipFromStorageFormat($ip) { + if(function_exists('inet_ntop')) { + // ipv4 & ipv6: + return @inet_ntop($ip); + } + // Only ipv4: + $unpacked = @unpack('Nlong',$ip); + if(isset($unpacked['long'])) { + return @long2ip($unpacked['long']); + } + return null; + } + + function getConfig($key, $subkey=null) { + if($subkey) + return $this->_config[$key][$subkey]; + else + return $this->_config[$key]; + } + + function setConfig($key, $subkey, $value) { + if($subkey) { + if(!isset($this->_config[$key])) { + $this->_config[$key] = array(); + } + $this->_config[$key][$subkey] = $value; + } else { + $this->_config[$key] = $value; + } + } + + function getLang($key=null) { + if(!$this->_lang) { + // Include the language file: + $lang = null; + require(AJAX_CHAT_PATH.'lib/lang/'.$this->getLangCode().'.php'); + $this->_lang = &$lang; + } + if($key === null) + return $this->_lang; + if(isset($this->_lang[$key])) + return $this->_lang[$key]; + return null; + } + + function getChatURL() { + if(defined('AJAX_CHAT_URL')) { + return AJAX_CHAT_URL; + } + + return + (isset($_SERVER['HTTPS']) ? 'https://' : 'http://'). + (isset($_SERVER['REMOTE_USER']) ? $_SERVER['REMOTE_USER'].'@' : ''). + (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : ($_SERVER['SERVER_NAME']. + (isset($_SERVER['HTTPS']) && $_SERVER['SERVER_PORT'] == 443 || $_SERVER['SERVER_PORT'] == 80 ? '' : ':'.$_SERVER['SERVER_PORT']))). + substr($_SERVER['SCRIPT_NAME'],0, strrpos($_SERVER['SCRIPT_NAME'], '/')+1); + } + + function getIDFromName($userName) { + $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); + if($userDataArray && isset($userDataArray[0])) { + return $userDataArray[0]['userID']; + } + return null; + } + + function getNameFromID($userID) { + $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); + if($userDataArray && isset($userDataArray[0])) { + return $userDataArray[0]['userName']; + } + return null; + } + + function getChannelFromID($userID) { + $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); + if($userDataArray && isset($userDataArray[0])) { + return $userDataArray[0]['channel']; + } + return null; + } + + function getIPFromID($userID) { + $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); + if($userDataArray && isset($userDataArray[0])) { + return $userDataArray[0]['ip']; + } + return null; + } + + function getRoleFromID($userID) { + $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); + if($userDataArray && isset($userDataArray[0])) { + return $userDataArray[0]['userRole']; + } + return null; + } + + function getChannelNames() { + return array_flip($this->getChannels()); + } + + function getChannelIDFromChannelName($channelName) { + if(!$channelName) + return null; + $channels = $this->getAllChannels(); + if(array_key_exists($channelName,$channels)) { + return $channels[$channelName]; + } + $channelID = null; + // Check if the requested channel is the own private channel: + if($channelName == $this->getPrivateChannelName()) { + return $this->getPrivateChannelID(); + } + // Try to retrieve a private room ID: + $strlenChannelName = $this->stringLength($channelName); + $strlenPrefix = $this->stringLength($this->getConfig('privateChannelPrefix')); + $strlenSuffix = $this->stringLength($this->getConfig('privateChannelSuffix')); + if($this->subString($channelName,0,$strlenPrefix) == $this->getConfig('privateChannelPrefix') + && $this->subString($channelName,$strlenChannelName-$strlenSuffix) == $this->getConfig('privateChannelSuffix')) { + $userName = $this->subString( + $channelName, + $strlenPrefix, + $strlenChannelName-($strlenPrefix+$strlenSuffix) + ); + $userID = $this->getIDFromName($userName); + if($userID !== null) { + $channelID = $this->getPrivateChannelID($userID); + } + } + return $channelID; + } + + function getChannelNameFromChannelID($channelID) { + foreach($this->getAllChannels() as $key=>$value) { + if($value == $channelID) { + return $key; + } + } + // Try to retrieve a private room name: + if($channelID == $this->getPrivateChannelID()) { + return $this->getPrivateChannelName(); + } + $userName = $this->getNameFromID($channelID-$this->getConfig('privateChannelDiff')); + if($userName === null) { + return null; + } + return $this->getPrivateChannelName($userName); + } + + function getChannelName() { + return $this->getChannelNameFromChannelID($this->getChannel()); + } + + function getPrivateChannelName($userName=null) { + if($userName === null) { + $userName = $this->getUserName(); + } + return $this->getConfig('privateChannelPrefix').$userName.$this->getConfig('privateChannelSuffix'); + } + + function getPrivateChannelID($userID=null) { + if($userID === null) { + $userID = $this->getUserID(); + } + return $userID + $this->getConfig('privateChannelDiff'); + } + + function getPrivateMessageID($userID=null) { + if($userID === null) { + $userID = $this->getUserID(); + } + return $userID + $this->getConfig('privateMessageDiff'); + } + + function isAllowedToSendPrivateMessage() { + if($this->getConfig('allowPrivateMessages') || $this->getUserRole() == AJAX_CHAT_ADMIN) { + return true; + } + return false; + } + + function isAllowedToCreatePrivateChannel() { + if($this->getConfig('allowPrivateChannels')) { + switch($this->getUserRole()) { + case AJAX_CHAT_USER: + return true; + case AJAX_CHAT_MODERATOR: + return true; + case AJAX_CHAT_ADMIN: + return true; + default: + return false; + } + } + return false; + } + + function isAllowedToListHiddenUsers() { + // Hidden users are users within private or restricted channels: + switch($this->getUserRole()) { + case AJAX_CHAT_MODERATOR: + return true; + case AJAX_CHAT_ADMIN: + return true; + default: + return false; + } + } + + function isUserOnline($userID=null) { + $userID = ($userID === null) ? $this->getUserID() : $userID; + $userDataArray = $this->getOnlineUsersData(null,'userID',$userID); + if($userDataArray && count($userDataArray) > 0) { + return true; + } + return false; + } + + function isUserNameInUse($userName=null) { + $userName = ($userName === null) ? $this->getUserName() : $userName; + $userDataArray = $this->getOnlineUsersData(null,'userName',$userName); + if($userDataArray && count($userDataArray) > 0) { + return true; + } + return false; + } + + function isUserBanned($userName, $userID=null, $ip=null) { + if($userID !== null) { + $bannedUserDataArray = $this->getBannedUsersData('userID',$userID); + if($bannedUserDataArray && isset($bannedUserDataArray[0])) { + return true; + } + } + if($ip !== null) { + $bannedUserDataArray = $this->getBannedUsersData('ip',$ip); + if($bannedUserDataArray && isset($bannedUserDataArray[0])) { + return true; + } + } + $bannedUserDataArray = $this->getBannedUsersData('userName',$userName); + if($bannedUserDataArray && isset($bannedUserDataArray[0])) { + return true; + } + return false; + } + + function isMaxUsersLoggedIn() { + if(count($this->getOnlineUsersData()) >= $this->getConfig('maxUsersLoggedIn')) { + return true; + } + return false; + } + + function validateChannel($channelID) { + if($channelID === null) { + return false; + } + // Return true for normal channels the user has acces to: + if(in_array($channelID, $this->getChannels())) { + return true; + } + // Return true if the user is allowed to join his own private channel: + if($channelID == $this->getPrivateChannelID() && $this->isAllowedToCreatePrivateChannel()) { + return true; + } + // Return true if the user has been invited to a restricted or private channel: + if(in_array($channelID, $this->getInvitations())) { + return true; + } + // No valid channel, return false: + return false; + } + + function createGuestUserName() { + $maxLength = $this->getConfig('userNameMaxLength') + - $this->stringLength($this->getConfig('guestUserPrefix')) + - $this->stringLength($this->getConfig('guestUserSuffix')); + + // seed with microseconds since last "whole" second: + mt_srand((double)microtime()*1000000); + + // Create a random userName using numbers between 100000 and 999999: + $userName = substr(mt_rand(100000, 999999), 0, $maxLength); + + return $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); + } + + // Guest userIDs must not interfere with existing userIDs and must be lower than privateChannelDiff: + function createGuestUserID() { + // seed with microseconds since last "whole" second: + mt_srand((double)microtime()*1000000); + + return mt_rand($this->getConfig('minGuestUserID'), $this->getConfig('privateChannelDiff')-1); + } + + function getGuestUser() { + if(!$this->getConfig('allowGuestLogins')) + return null; + + if($this->getConfig('allowGuestUserName')) { + $maxLength = $this->getConfig('userNameMaxLength') + - $this->stringLength($this->getConfig('guestUserPrefix')) + - $this->stringLength($this->getConfig('guestUserSuffix')); + + // Trim guest userName: + $userName = $this->trimString($this->getRequestVar('userName'), null, $maxLength, true, true); + + // If given userName is invalid, create one: + if(!$userName) { + $userName = $this->createGuestUserName(); + } else { + // Add the guest users prefix and suffix to the given userName: + $userName = $this->getConfig('guestUserPrefix').$userName.$this->getConfig('guestUserSuffix'); + } + } else { + $userName = $this->createGuestUserName(); + } + + $userData = array(); + $userData['userID'] = $this->createGuestUserID(); + $userData['userName'] = $userName; + $userData['userRole'] = AJAX_CHAT_GUEST; + return $userData; + } + + function getCustomVar($key) { + if(!isset($this->_customVars)) + $this->_customVars = array(); + if(!isset($this->_customVars[$key])) + return null; + return $this->_customVars[$key]; + } + + function setCustomVar($key, $value) { + if(!isset($this->_customVars)) + $this->_customVars = array(); + $this->_customVars[$key] = $value; + } + + // Override to replace custom template tags: + // Return the replacement for the given tag (and given tagContent) + function replaceCustomTemplateTags($tag, $tagContent) { + return null; + } + + // Override to initialize custom configuration settings: + function initCustomConfig() { + } + + // Override to add custom request variables: + // Add values to the request variables array: $this->_requestVars['customVariable'] = null; + function initCustomRequestVars() { + } + + // Override to add custom session code right after the session has been started: + function initCustomSession() { + } + + // Override, to parse custom info requests: + // $infoRequest contains the current info request + // Add info responses using the method addInfoMessage($info, $type) + function parseCustomInfoRequest($infoRequest) { + } + + // Override to replace custom text: + // Return replaced text + // $text contains the whole message + function replaceCustomText(&$text) { + return $text; + } + + // Override to add custom commands: + // Return true if a custom command has been successfully parsed, else false + // $text contains the whole message, $textParts the message split up as words array + function parseCustomCommands($text, $textParts) { + return false; + } + + // Override to perform custom actions on new messages: + // Return true if message may be inserted, else false + // $text contains the whole message + function onNewMessage($text) { + return true; + } + + // Override to perform custom actions on new messages: + // Method to set the style cookie depending on user data + function setStyle() { + } + + // Override: + // Returns true if the userID of the logged in user is identical to the userID of the authentication system + // or the user is authenticated as guest in the chat and the authentication system + function revalidateUserID() { + return true; + } + + // Override: + // Returns an associative array containing userName, userID and userRole + // Returns null if login is invalid + function getValidLoginUserData() { + // Check if we have a valid registered user: + if(false) { + // Here is the place to check user authentication + } else { + // Guest users: + return $this->getGuestUser(); + } + } + + // Override: + // Store the channels the current user has access to + // Make sure channel names don't contain any whitespace + function &getChannels() { + if($this->_channels === null) { + $this->_channels = $this->getAllChannels(); + } + return $this->_channels; + } + + // Override: + // Store all existing channels + // Make sure channel names don't contain any whitespace + function &getAllChannels() { + if($this->_allChannels === null) { + $this->_allChannels = array(); + + // Default channel, public to everyone: + $this->_allChannels[$this->trimChannelName($this->getConfig('defaultChannelName'))] = $this->getConfig('defaultChannelID'); + } + return $this->_allChannels; + } + +} ?> \ No newline at end of file diff --git a/chat/lib/config.php.example b/chat/lib/config.php.example index 023adcd..4cf4b21 100644 --- a/chat/lib/config.php.example +++ b/chat/lib/config.php.example @@ -8,6 +8,8 @@ */ // Define AJAX Chat user roles: +define('AJAX_CHAT_BANNED', 6); +define('AJAX_CHAT_CUSTOM', 5); define('AJAX_CHAT_CHATBOT', 4); define('AJAX_CHAT_ADMIN', 3); define('AJAX_CHAT_MODERATOR', 2); diff --git a/chat/lib/lang/ar.php b/chat/lib/lang/ar.php index ebcc7a0..4b437d4 100644 --- a/chat/lib/lang/ar.php +++ b/chat/lib/lang/ar.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/ca.php b/chat/lib/lang/ca.php index 87b1be5..b0ead60 100644 --- a/chat/lib/lang/ca.php +++ b/chat/lib/lang/ca.php @@ -1,126 +1,126 @@ - + diff --git a/chat/lib/lang/cy.php b/chat/lib/lang/cy.php index 4fa92f4..3490206 100644 --- a/chat/lib/lang/cy.php +++ b/chat/lib/lang/cy.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/es.php b/chat/lib/lang/es.php index 65e8e68..2b9cdc3 100644 --- a/chat/lib/lang/es.php +++ b/chat/lib/lang/es.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/fr.php b/chat/lib/lang/fr.php index e81819e..4d88709 100644 --- a/chat/lib/lang/fr.php +++ b/chat/lib/lang/fr.php @@ -1,126 +1,126 @@ - + diff --git a/chat/lib/lang/ka.php b/chat/lib/lang/ka.php index cd0d09c..09efc62 100644 --- a/chat/lib/lang/ka.php +++ b/chat/lib/lang/ka.php @@ -1,4 +1,4 @@ - + diff --git a/chat/lib/lang/nl.php b/chat/lib/lang/nl.php index 19c5502..a652d30 100644 --- a/chat/lib/lang/nl.php +++ b/chat/lib/lang/nl.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/no.php b/chat/lib/lang/no.php index 3a40360..9febc58 100644 --- a/chat/lib/lang/no.php +++ b/chat/lib/lang/no.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/pl.php b/chat/lib/lang/pl.php index ba64036..7fedea5 100644 --- a/chat/lib/lang/pl.php +++ b/chat/lib/lang/pl.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/lang/sl.php b/chat/lib/lang/sl.php index 584b98a..00f4551 100644 --- a/chat/lib/lang/sl.php +++ b/chat/lib/lang/sl.php @@ -1,125 +1,125 @@ - + diff --git a/chat/lib/template/loggedIn.html b/chat/lib/template/loggedIn.html index 71d79f9..b7118ce 100644 --- a/chat/lib/template/loggedIn.html +++ b/chat/lib/template/loggedIn.html @@ -3,21 +3,11 @@ + [LANG]title[/LANG] - [STYLE_SHEETS/] - + @@ -25,29 +15,126 @@ - +
-
-

[LANG]title[/LANG]

-
+

[LANG]title[/LANG]

- - - - - - - -
-
- -
-
- -
-
- 0/[MESSAGE_TEXT_MAX_LENGTH/] - -
-
-
- - - - - - - - + +
+ + + + + + +
+
- -
- - - - - -
-
-

[LANG]onlineUsers[/LANG]

-
-
- + +
+ +
+
+ 0/[MESSAGE_TEXT_MAX_LENGTH/] + +
+
+
+ + + + + + + + + +
+ +
+ + + + + +
+ @@ -390,5 +455,4 @@

[LANG]settings[/LANG]

- diff --git a/chat/lib/template/loggedOut.html b/chat/lib/template/loggedOut.html index ba8a8a4..5e225aa 100644 --- a/chat/lib/template/loggedOut.html +++ b/chat/lib/template/loggedOut.html @@ -3,18 +3,10 @@ + [LANG]title[/LANG] - [STYLE_SHEETS/] - @@ -23,8 +15,8 @@ function initializeLoginPage() { document.getElementById('userNameField').focus(); if(!ajaxChat.isCookieEnabled()) { - var node = document.createElement('div'); - var text = document.createTextNode(ajaxChatLang['errorCookiesRequired']); + var node = document.createElement('div'), + text = document.createTextNode(ajaxChatLang['errorCookiesRequired']); node.appendChild(text); document.getElementById('errorContainer').appendChild(node); } @@ -40,39 +32,32 @@ // ]]> - - +
-
-

[LANG]title[/LANG]

-
+

[LANG]title[/LANG]

+
[ERROR_MESSAGES/]
-
- - -

-
-

-
-

-
-

-
-
-
* [LANG]registeredUsers[/LANG]
-
+ + +

+
+

+
+

+
+

+
+
+
* [LANG]registeredUsers[/LANG]
-
[ERROR_MESSAGES/]
- \ No newline at end of file diff --git a/chat/lib/template/logs.html b/chat/lib/template/logs.html index 073ae2b..4aa98d0 100644 --- a/chat/lib/template/logs.html +++ b/chat/lib/template/logs.html @@ -5,46 +5,27 @@ [LANG]logsTitle[/LANG] - [STYLE_SHEETS/] - - +
-
-

[LANG]logsTitle[/LANG]

-
+

[LANG]logsTitle[/LANG]

@@ -257,10 +236,12 @@

[LANG]settings[/LANG]

+ + @@ -279,5 +260,4 @@

[LANG]settings[/LANG]

- - + \ No newline at end of file diff --git a/chat/readme.html b/chat/readme.html index e6137ab..d733b16 100644 --- a/chat/readme.html +++ b/chat/readme.html @@ -1,469 +1,469 @@ - - - - - AJAX Chat Readme - - - - - -
-
-

- AJAX Chat - - v 0.8.8 standalone ( blueimp.net/ajax/ ) - -

-
-
- -
- - - -
-

Version Information

-
- This is the standalone version of blueimp's AJAX Chat designed to run on its own, without another web application.
- If you want to integrate AJAX Chat with one of the forums we support, go back and choose the right version.
- This version is good for customizing your own integration, or using on its own.

- -

- AJAX stands for "Asynchronous JavaScript and XML".
- The AJAX Chat client (your browser) uses JavaScript to query the web server for updates.
- Instead of delivering a complete HTML page only updated data is sent in XML format.
- By using JavaScript the chat page can be updated without having to reload the whole page.
- PHP is used to communicate with the database and authenticate users. -

-
-
- -

1. Requirements

-
- - - - - - - - - -
Server-SideClient-Side
- PHP >= 5
- MySQL >= 4
- Ruby >= 1.8 (optional) -
- Enabled JavaScript
- Enabled Cookies
- Flash Plugin >= 9 (optional) -
-
- -

2. Installation

-
-

Download your preferred version of AJAX Chat and unzip the file on your computer.

- -
-

Use a Proper Text Editor!

- In order to edit PHP files you will need a good text editor. You should not use Windows notepad, wordpad, or Microsoft Word to edit PHP files. These programs will add something called a byte-order-mark (BOM) to the files and this may prevent chat from functioning properly. - We recommend using Notepad ++ ( http://notepad-plus-plus.org ) for editing all files. It also has the benefit of color-coding your files so you can edit them more easily.
- If you get an error message like "Cannot modify header information - headers already sent" it is likely because you have used one of the above programs to edit files. -
- -

Configure Database Settings

-
-

- The first and most important thing you need to do is tell AJAX Chat how to connect to your database. This, and all core settings must be located inside the file lib/config.php.
- You need to create this file.
- An example config.php file can be found in lib/config.php.example that shipped with chat.
- Duplicate this file and save it as config.php (or just delete .example from the end of the file name) and then fill out at least the following four fields in the file: -

-

- - $config['dbConnection']['host'] = 'your_database_hostname';
- $config['dbConnection']['user'] = 'your_database_username';
- $config['dbConnection']['pass'] = 'your_database_password';
- $config['dbConnection']['name'] = 'your_database_name'; -
-

-

Sufficed to say you need this information. Talk to your hosting provider if you don't know.

-

- In most cases, chat will function with only these fields filled out and you can proceed to the next step.
-

-

- If your host does not use mysqli you will need to change the connection type field:
- $config['dbConnection']['type'] = null;
- If this is set to "null" it defaults to "mysqli" if existing, else to "mysql". In most cases this field can be left as null.
-
- You can reference an existing database connection link or object by changing:
- $config['dbConnection']['link'] = null;
- If this is set to null, a new database connection is created. -

-
- -

Choose Your Channel Settings

-
-

Edit the file lib/data/channels.php.
- We have provided you with two sample channels, named public and private. You can add your own, or leave it as-is.
- Channels follow the following format: -
- $channels[channel id] = 'channel name'; - Each channel must have a unique channel id number and a unique name.
- Whitespace in the channel names will be converted to the underscore "_".

-
- -

Add Your Users

-
-

- The Standalone version of chat uses a php file to store users and rooms while the database is used for chat messages, invites and bans.
- The integration versions typically make use of a database for users and rooms. If you desire a way to manage users without having to edit a php file, consider using an integration version. -

-

- Edit users in lib/data/users.php.
- Users follow the following format: -

-

$users[user id] = array();
- $users[user id]['userRole'] = AJAX_CHAT_ROLE;
- $users[user id]['userName'] = 'user name';
- $users[user id]['password'] = 'user password';
- $users[user id]['channels'] = array(allowed channel ids);

- Each user must have a unique user id number and a unique name.
- The first user in the list (user id 0) is used for the guest user settings. All guest users will have access to the channels set for this user and the user role AJAX_CHAT_GUEST.
- Registered users can have the user roles AJAX_CHAT_USER, AJAX_CHAT_MODERATOR or AJAX_CHAT_ADMIN. (this is case sensitive, type it exactly)
- The list of channels a user has access to can be set for each user individually. Channel id's are separated by commas. eg: array(0,1,23); allows channels 0, 1 and 23.
- Whitespace in the user names will be converted to the underscore "_". -

-
- -

Upload to Your Server

-
-

Upload the chat folder to your server somewhere under your document root:
- e.g. http://example.org/path/to/chat/

-
-

Create the Database Tables

-
-

There are two options available to you to create the database. The first, and usually the easiest option, is to run the installation script included with AJAX Chat. Alternatively, you may use a database tool like PHPMyAdmin to manually create the tables.

-
    -
  1. - To use the installation script, visit the following URL in your browser:
    - http://example.org/path/to/chat/install.php
    - Where - "http://example.org/path/to/chat/" is the real URL to your chat directory.
    - Be sure to delete the install.php file after you have completed this step! -
  2. -
  3. To install it manually using PHPMyAdmin or a similar tool, copy the contents of the chat.sql file and run it as a query.
  4. -
-

Either of these methods will create the tables your database needs to store chat messages and other information.

-
- -

Delete the Installation Script

-
-

Delete the file install.php from the chat directory on your server. You may also delete the file chat.sql.

-
- -

Congradulation! You Are Winner!

-
-

Yay! You're done! To test your chat, navigate to your chat URL in a browser: http://example.org/path/to/chat/index.php
-
You are now free to customize chat to further suit your needs.

-
-
- -

3. Configuring and Customizing

-
-

Configuration Files

-
-

AJAX Chat is fully customizable and contains two configuration files:

-
    -
  1. lib/config.php: This file contains the core configuration options for chat. Essential options for configuring the database, security, available languages, etc, are found here.
  2. -
  3. js/config.js: This file contains client side settings that change your users' default options in chat. Many of these settings can be changed by users in their options but some (like the refresh rate) cannot.
  4. -
-

Both of these files are well commented with information on what the settings mean.

-
- -

Customizing the Layout

-
-

The layout of AJAX Chat is fully customizable by using CSS (Cascaded Style Sheets).
- AJAX Chat comes with a predefined set of styles. To add your own style, do the following:

-
    -
  1. Add a new CSS file (e.g. mystyle.css) by copying one of the existing styles from the CSS directory.
  2. -
  3. Edit your file (css/mystyle.css) and adjust the CSS settings to your liking.
  4. -
  5. Add the name of your style without file extension to the available styles in lib/config.php:
    - // Available styles:
    - $config['styleAvailable'] = array('mystyle','beige','black','grey');
    - // Default style:
    - $config['styleDefault'] = 'mystyle';
  6. -
-

To further customize the layout you can adjust the template files in lib/template/.

-

Make sure you are creating valid XHTML, else you will produce errors in modern browsers.
- This is due to the page content-type served as "application/xhtml+xml".
- Using this content-type improves performance when manipulating the Document Object Model (DOM).

-

If for some reason you cannot create valid XHTML you can force a HTML content-type.
- Just edit lib/config.php and set the following option:

-

$config['contentType'] = 'text/html';

-
- -

Adjusting the Language Settings

-
-

AJAX Chat comes with two language file directories:

-
    -
  1. js/lang/: This directory contains the language files used for the chat messages localization. These are JavaScript files with the extension ".js".
  2. -
  3. lib/lang/: This directory contains the language files used for the template output. These are PHP files with the extension ".php".
  4. -
-

Many languages are already included with the download and you can customize them by editing these files.
- For each language, you need a file in both of these directories, with the language code as file name (such as en.js and en.php)..
- The language code is used following the ISO 639 standards.

-

The files for the english (language code "en") localization are js/lang/en.js and lib/lang/en.php.

-

If you create your own localization, you must put the files in the correct folders and then make two changes to config.php:

-
    -
  1. Add the language code (this must match the filename you chose for the language. Remember to use commas correctly to separate multiple language codes):
    - $config['langAvailable'] = array('en');
  2. -
  3. Add the language name (this is what users see in the dropdown menu to choose the language):
    - $config['langNames'] = array('en'=>'English');
  4. -
-

To avoid errors, you should follow these rules:

-
    -
  1. Make sure you encode your localization files in UTF-8 (without Byte-order mark).
  2. -
  3. Don't use HTML entities in your localization files.
  4. -
  5. Don't remove any "%s" inside the JavaScript language files - these are filled with dynamic data.
  6. -
-
- -

Adding Features

-
-

AJAX Chat is designed with numerous hooks and overrides available to improve core functionality without requiring you to edit the core files. - With an intermediate understading of PHP and javascript you can modify your chat to suit your needs.

-

Have a look through a few examples available on the wiki: - https://github.com/Frug/AJAX-Chat/wiki/General-modifications -

-
- -
- -

4. Logs

-
-

Accessing the Logs

-
-

By default, AJAX Chat stores all chat messages in the database.
- To access the logs you have to add the GET parameter view=logs to your chat url (add ?view=logs to the end of the url):

-

e.g. http://example.org/path/to/chat/?view=logs

-

If you are not already logged in, you have to login as administrator to access the logs.

-

The log view enables you to monitor the latest chat messages on all channels.
- It is also possible to view the logs of private rooms and private messages.
- You have the option to filter the logs by date, time and search strings.

-

The search filter accepts MySQL style regular expressions as described here: http://dev.mysql.com/doc/refman/5.1/en/regexp.html
- You can search for IPs, using the following syntax: ip=127.0.0.1

-
-
- -

5. Shoutbox

-
-

AJAX Chat is also usable as shoutbox - this is a short guide on how to set it up:

- -

Shoutbox Stylesheet

-
-

Add the following line to the stylesheet (CSS) of all pages displaying the shoutbox:

-

@import url("http://example.org/path/to/chat/css/shoutbox.css");

-

Replace http://example.org/path/to/chat/ with the URL to the chat.
- Modify css/shoutbox.css to your liking.

-
- -

Shoutbox Function

-
-

Add the following function to your PHP code:

- -
-<?php
-function getShoutBoxContent() {
-// URL to the chat directory:
-if(!defined('AJAX_CHAT_URL')) {
-	define('AJAX_CHAT_URL', './chat/');
-}
-
-// Path to the chat directory:
-if(!defined('AJAX_CHAT_PATH')) {
-	define('AJAX_CHAT_PATH', realpath(dirname($_SERVER['SCRIPT_FILENAME']).'/chat').'/');
-}
-
-// Validate the path to the chat:
-if(@is_file(AJAX_CHAT_PATH.'lib/classes.php')) {
-	
-	// Include Class libraries:
-	require_once(AJAX_CHAT_PATH.'lib/classes.php');
-	
-	// Initialize the shoutbox:
-	$ajaxChat = new CustomAJAXChatShoutBox();
-	
-	// Parse and return the shoutbox template content:
-	return $ajaxChat->getShoutBoxContent();
-}
-
-return null;
-}
-?>
-
- -

Make sure AJAX_CHAT_URL and AJAX_CHAT_PATH point to the chat directory.

-
- -

Shoutbox Output

-
-

Display the shoutbox content using the shoutbox function:

-

<div style="width:200px;"><?php echo getShoutBoxContent(); ?></div>

-
-
- -

6. Socket Server

-
-

- This part of the setup is OPTIONAL and meant for experienced users only.
- The Socket Server is no longer actively supported and may not function correctly out of the box. - Later versions of AJAX Chat may have this socket server implementation replaced with something else.
- Please do not report bugs regarding the socket server - you're on your own with this one! -

- -

Using the AJAX technology alone the chat clients have to permanently pull updates from the server.
- This is due to AJAX being a web technology and HTTP being a stateless protocol.
- Events pushed from server-side need a permanent or long-lasting socket connection between clients and server.
- This requires either a custom HTTP server (called "comet") or another custom socket server.

-

AJAX Chat uses a JavaScript-to-Flash bridge to establish a permanent socket connection from client side.
- The JavaScript-to-Flash bridge requires a Flash plugin >= 9 installed on the user browser.
- Clients without this requirement will fall back to pull the server for updates.

-

Installation

-
-

The socket server coming with AJAX Chat is implemented in Ruby.
- You need to be able to run a Ruby script as a service to run the socket server.
- To be able to start the service, the script files in the socket/ directory have to be executable:

-

$ chmod +x server
- $ chmod +x server.rb

-

"server" is a simple bash script to start and stop a service.
- "server.rb" is the ruby socket server script.
- "server.conf" is a configuration file - each setting is explained with a comment.

-

To start the service, execute the "server" script with the parameter "start":

-

$ ./server start

-

This will create two additional files:

-

"server.pid" contains the process id of the service.
- "server.log" is filled with the socket server log.

-

To monitor the socket server logs, you can use the "tail" command included in most GNU/Linux distributions:

-

$ tail -f server.log

-

By default only errors and start/stop of the server are logged.
- To get more detailed logs configure the log level by editing the configuration file.

-

To stop the service, execute the "server" script with the parameter "stop":

-

$ ./server stop

-

If the socket server is running, you have to enable the following option in lib/config.php:

-

$config['socketServerEnabled'] = true;
-
- This tells the server-side chat script to broadcast chat messages via the socket server.
- Chat clients will establish a permanent connection to the socket server to listen for chat messages.

-

By default only local clients (127.0.0.1,::1) may broadcast messages.
- Clients allowed to broadcast messages may also handle the channel authentication.
- If your socket server is running on another host you should set the broadcast_clients option to the chat server IP.

-

Using the socket server increases response time while improving server performance at the same time.

-
- -

Flash Permissions

-
-

- Since Flash 9.0.115.0 and all Flash 10 versions, permissions for creating sockets using Flash have changed.
- Now an explicit permission (using xml-syntax) is required for creating socket connections.
- In the current state, socket server won't work with the newest Flash versions.
- You will get a "Flash security error" in the browser. -

-

- A solution is to use a policy-files server which will listen to connections in port 843 in the server.
- Each time a client tries to connect to the chat, the Flash client will request the policy authorization to the server.
- The policy-files server is downloadable from http://ammonlauritzen.com/FlashPolicyService-09b.zip
- It works with FF3 and IE7 (not yet tested in other browsers). -

-

A more detailed explanation can be found here:

-

* http://ammonlauritzen.com/blog/2007/12/13/new-flash-security-policies/
- * http://ammonlauritzen.com/blog/2008/04/22/flash-policy-service-daemon/
-

-

Official Adobe documentation:

-

* http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html
- * http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security_04.html

-
-

 

-
- -

7. Support

-
-

- Please do not email the devs with support questions.
- For further documentation and some examples, check out our github wiki.
- For general support questions use our google group.
- For specific bug reports and a list of pending issues view our github project.
-

-
- - -
-

- Your donations contribute to the growth and development of this project and are always appreciated.
-

- - - - -
- I'm on gittip at https://www.gittip.com/Frug -

-
- -

9. License

-
-

Bluimp's AJAX Chat is released under a Modified MIT License.

-

You should also find this license included with your download of this project.

-
- -

back to top

- -
- + + + + + AJAX Chat Readme + + + + + +
+
+

+ AJAX Chat + + v 0.8.8 standalone ( blueimp.net/ajax/ ) + +

+
+
+ +
+ + + +
+

Version Information

+
+ This is the standalone version of blueimp's AJAX Chat designed to run on its own, without another web application.
+ If you want to integrate AJAX Chat with one of the forums we support, go back and choose the right version.
+ This version is good for customizing your own integration, or using on its own. + +

+ AJAX stands for "Asynchronous JavaScript and XML".
+ The AJAX Chat client (your browser) uses JavaScript to query the web server for updates.
+ Instead of delivering a complete HTML page only updated data is sent in XML format.
+ By using JavaScript the chat page can be updated without having to reload the whole page.
+ PHP is used to communicate with the database and authenticate users. +

+
+
+ +

1. Requirements

+
+ + + + + + + + + +
Server-SideClient-Side
+ PHP >= 5
+ MySQL >= 4
+ Ruby >= 1.8 (optional) +
+ Enabled JavaScript
+ Enabled Cookies
+ Flash Plugin >= 9 (optional) +
+
+ +

2. Installation

+
+

Download your preferred version of AJAX Chat and unzip the file on your computer.

+ +
+

Use a Proper Text Editor!

+ In order to edit PHP files you will need a good text editor. You should not use Windows notepad, wordpad, or Microsoft Word to edit PHP files. These programs will add something called a byte-order-mark (BOM) to the files and this may prevent chat from functioning properly. + We recommend using Notepad ++ ( http://notepad-plus-plus.org ) for editing all files. It also has the benefit of color-coding your files so you can edit them more easily.
+ If you get an error message like "Cannot modify header information - headers already sent" it is likely because you have used one of the above programs to edit files. +
+ +

Configure Database Settings

+
+

+ The first and most important thing you need to do is tell AJAX Chat how to connect to your database. This, and all core settings must be located inside the file lib/config.php.
+ You need to create this file.
+ An example config.php file can be found in lib/config.php.example that shipped with chat.
+ Duplicate this file and save it as config.php (or just delete .example from the end of the file name) and then fill out at least the following four fields in the file: +

+

+ + $config['dbConnection']['host'] = 'your_database_hostname';
+ $config['dbConnection']['user'] = 'your_database_username';
+ $config['dbConnection']['pass'] = 'your_database_password';
+ $config['dbConnection']['name'] = 'your_database_name'; +
+

+

Sufficed to say you need this information. Talk to your hosting provider if you don't know.

+

+ In most cases, chat will function with only these fields filled out and you can proceed to the next step.
+

+

+ If your host does not use mysqli you will need to change the connection type field:
+ $config['dbConnection']['type'] = null;
+ If this is set to "null" it defaults to "mysqli" if existing, else to "mysql". In most cases this field can be left as null.
+
+ You can reference an existing database connection link or object by changing:
+ $config['dbConnection']['link'] = null;
+ If this is set to null, a new database connection is created. +

+
+ +

Choose Your Channel Settings

+
+

Edit the file lib/data/channels.php.
+ We have provided you with two sample channels, named public and private. You can add your own, or leave it as-is.
+ Channels follow the following format: +
+ $channels[channel id] = 'channel name'; + Each channel must have a unique channel id number and a unique name.
+ Whitespace in the channel names will be converted to the underscore "_".

+
+ +

Add Your Users

+
+

+ The Standalone version of chat uses a php file to store users and rooms while the database is used for chat messages, invites and bans.
+ The integration versions typically make use of a database for users and rooms. If you desire a way to manage users without having to edit a php file, consider using an integration version. +

+

+ Edit users in lib/data/users.php.
+ Users follow the following format: +

+

$users[user id] = array();
+ $users[user id]['userRole'] = AJAX_CHAT_ROLE;
+ $users[user id]['userName'] = 'user name';
+ $users[user id]['password'] = 'user password';
+ $users[user id]['channels'] = array(allowed channel ids);

+ Each user must have a unique user id number and a unique name.
+ The first user in the list (user id 0) is used for the guest user settings. All guest users will have access to the channels set for this user and the user role AJAX_CHAT_GUEST.
+ Registered users can have the user roles AJAX_CHAT_USER, AJAX_CHAT_MODERATOR or AJAX_CHAT_ADMIN. (this is case sensitive, type it exactly)
+ The list of channels a user has access to can be set for each user individually. Channel id's are separated by commas. eg: array(0,1,23); allows channels 0, 1 and 23.
+ Whitespace in the user names will be converted to the underscore "_". +

+
+ +

Upload to Your Server

+
+

Upload the chat folder to your server somewhere under your document root:
+ e.g. http://example.org/path/to/chat/

+
+

Create the Database Tables

+
+

There are two options available to you to create the database. The first, and usually the easiest option, is to run the installation script included with AJAX Chat. Alternatively, you may use a database tool like PHPMyAdmin to manually create the tables.

+
    +
  1. + To use the installation script, visit the following URL in your browser:
    + http://example.org/path/to/chat/install.php
    + Where + "http://example.org/path/to/chat/" is the real URL to your chat directory.
    + Be sure to delete the install.php file after you have completed this step! +
  2. +
  3. To install it manually using PHPMyAdmin or a similar tool, copy the contents of the chat.sql file and run it as a query.
  4. +
+

Either of these methods will create the tables your database needs to store chat messages and other information.

+
+ +

Delete the Installation Script

+
+

Delete the file install.php from the chat directory on your server. You may also delete the file chat.sql.

+
+ +

Congradulation! You Are Winner!

+
+

Yay! You're done! To test your chat, navigate to your chat URL in a browser: http://example.org/path/to/chat/index.php
+
You are now free to customize chat to further suit your needs.

+
+
+ +

3. Configuring and Customizing

+
+

Configuration Files

+
+

AJAX Chat is fully customizable and contains two configuration files:

+
    +
  1. lib/config.php: This file contains the core configuration options for chat. Essential options for configuring the database, security, available languages, etc, are found here.
  2. +
  3. js/config.js: This file contains client side settings that change your users' default options in chat. Many of these settings can be changed by users in their options but some (like the refresh rate) cannot.
  4. +
+

Both of these files are well commented with information on what the settings mean.

+
+ +

Customizing the Layout

+
+

The layout of AJAX Chat is fully customizable by using CSS (Cascaded Style Sheets).
+ AJAX Chat comes with a predefined set of styles. To add your own style, do the following:

+
    +
  1. Add a new CSS file (e.g. mystyle.css) by copying one of the existing styles from the CSS directory.
  2. +
  3. Edit your file (css/mystyle.css) and adjust the CSS settings to your liking.
  4. +
  5. Add the name of your style without file extension to the available styles in lib/config.php:
    + // Available styles:
    + $config['styleAvailable'] = array('mystyle','beige','black','grey');
    + // Default style:
    + $config['styleDefault'] = 'mystyle';
  6. +
+

To further customize the layout you can adjust the template files in lib/template/.

+

Make sure you are creating valid XHTML, else you will produce errors in modern browsers.
+ This is due to the page content-type served as "application/xhtml+xml".
+ Using this content-type improves performance when manipulating the Document Object Model (DOM).

+

If for some reason you cannot create valid XHTML you can force a HTML content-type.
+ Just edit lib/config.php and set the following option:

+

$config['contentType'] = 'text/html';

+
+ +

Adjusting the Language Settings

+
+

AJAX Chat comes with two language file directories:

+
    +
  1. js/lang/: This directory contains the language files used for the chat messages localization. These are JavaScript files with the extension ".js".
  2. +
  3. lib/lang/: This directory contains the language files used for the template output. These are PHP files with the extension ".php".
  4. +
+

Many languages are already included with the download and you can customize them by editing these files.
+ For each language, you need a file in both of these directories, with the language code as file name (such as en.js and en.php)..
+ The language code is used following the ISO 639 standards.

+

The files for the english (language code "en") localization are js/lang/en.js and lib/lang/en.php.

+

If you create your own localization, you must put the files in the correct folders and then make two changes to config.php:

+
    +
  1. Add the language code (this must match the filename you chose for the language. Remember to use commas correctly to separate multiple language codes):
    + $config['langAvailable'] = array('en');
  2. +
  3. Add the language name (this is what users see in the dropdown menu to choose the language):
    + $config['langNames'] = array('en'=>'English');
  4. +
+

To avoid errors, you should follow these rules:

+
    +
  1. Make sure you encode your localization files in UTF-8 (without Byte-order mark).
  2. +
  3. Don't use HTML entities in your localization files.
  4. +
  5. Don't remove any "%s" inside the JavaScript language files - these are filled with dynamic data.
  6. +
+
+ +

Adding Features

+
+

AJAX Chat is designed with numerous hooks and overrides available to improve core functionality without requiring you to edit the core files. + With an intermediate understading of PHP and javascript you can modify your chat to suit your needs.

+

Have a look through a few examples available on the wiki: + https://github.com/Frug/AJAX-Chat/wiki/General-modifications +

+
+ +
+ +

4. Logs

+
+

Accessing the Logs

+
+

By default, AJAX Chat stores all chat messages in the database.
+ To access the logs you have to add the GET parameter view=logs to your chat url (add ?view=logs to the end of the url):

+

e.g. http://example.org/path/to/chat/?view=logs

+

If you are not already logged in, you have to login as administrator to access the logs.

+

The log view enables you to monitor the latest chat messages on all channels.
+ It is also possible to view the logs of private rooms and private messages.
+ You have the option to filter the logs by date, time and search strings.

+

The search filter accepts MySQL style regular expressions as described here: http://dev.mysql.com/doc/refman/5.1/en/regexp.html
+ You can search for IPs, using the following syntax: ip=127.0.0.1

+
+
+ +

5. Shoutbox

+
+

AJAX Chat is also usable as shoutbox - this is a short guide on how to set it up:

+ +

Shoutbox Stylesheet

+
+

Add the following line to the stylesheet (CSS) of all pages displaying the shoutbox:

+

@import url("http://example.org/path/to/chat/css/shoutbox.css");

+

Replace http://example.org/path/to/chat/ with the URL to the chat.
+ Modify css/shoutbox.css to your liking.

+
+ +

Shoutbox Function

+
+

Add the following function to your PHP code:

+ +
+<?php
+function getShoutBoxContent() {
+// URL to the chat directory:
+if(!defined('AJAX_CHAT_URL')) {
+	define('AJAX_CHAT_URL', './chat/');
+}
+
+// Path to the chat directory:
+if(!defined('AJAX_CHAT_PATH')) {
+	define('AJAX_CHAT_PATH', realpath(dirname($_SERVER['SCRIPT_FILENAME']).'/chat').'/');
+}
+
+// Validate the path to the chat:
+if(@is_file(AJAX_CHAT_PATH.'lib/classes.php')) {
+	
+	// Include Class libraries:
+	require_once(AJAX_CHAT_PATH.'lib/classes.php');
+	
+	// Initialize the shoutbox:
+	$ajaxChat = new CustomAJAXChatShoutBox();
+	
+	// Parse and return the shoutbox template content:
+	return $ajaxChat->getShoutBoxContent();
+}
+
+return null;
+}
+?>
+
+ +

Make sure AJAX_CHAT_URL and AJAX_CHAT_PATH point to the chat directory.

+
+ +

Shoutbox Output

+
+

Display the shoutbox content using the shoutbox function:

+

<div style="width:200px;"><?php echo getShoutBoxContent(); ?></div>

+
+
+ +

6. Socket Server

+
+

+ This part of the setup is OPTIONAL and meant for experienced users only.
+ The Socket Server is no longer actively supported and may not function correctly out of the box. + Later versions of AJAX Chat may have this socket server implementation replaced with something else.
+ Please do not report bugs regarding the socket server - you're on your own with this one! +

+ +

Using the AJAX technology alone the chat clients have to permanently pull updates from the server.
+ This is due to AJAX being a web technology and HTTP being a stateless protocol.
+ Events pushed from server-side need a permanent or long-lasting socket connection between clients and server.
+ This requires either a custom HTTP server (called "comet") or another custom socket server.

+

AJAX Chat uses a JavaScript-to-Flash bridge to establish a permanent socket connection from client side.
+ The JavaScript-to-Flash bridge requires a Flash plugin >= 9 installed on the user browser.
+ Clients without this requirement will fall back to pull the server for updates.

+

Installation

+
+

The socket server coming with AJAX Chat is implemented in Ruby.
+ You need to be able to run a Ruby script as a service to run the socket server.
+ To be able to start the service, the script files in the socket/ directory have to be executable:

+

$ chmod +x server
+ $ chmod +x server.rb

+

"server" is a simple bash script to start and stop a service.
+ "server.rb" is the ruby socket server script.
+ "server.conf" is a configuration file - each setting is explained with a comment.

+

To start the service, execute the "server" script with the parameter "start":

+

$ ./server start

+

This will create two additional files:

+

"server.pid" contains the process id of the service.
+ "server.log" is filled with the socket server log.

+

To monitor the socket server logs, you can use the "tail" command included in most GNU/Linux distributions:

+

$ tail -f server.log

+

By default only errors and start/stop of the server are logged.
+ To get more detailed logs configure the log level by editing the configuration file.

+

To stop the service, execute the "server" script with the parameter "stop":

+

$ ./server stop

+

If the socket server is running, you have to enable the following option in lib/config.php:

+

$config['socketServerEnabled'] = true;
+
+ This tells the server-side chat script to broadcast chat messages via the socket server.
+ Chat clients will establish a permanent connection to the socket server to listen for chat messages.

+

By default only local clients (127.0.0.1,::1) may broadcast messages.
+ Clients allowed to broadcast messages may also handle the channel authentication.
+ If your socket server is running on another host you should set the broadcast_clients option to the chat server IP.

+

Using the socket server increases response time while improving server performance at the same time.

+
+ +

Flash Permissions

+
+

+ Since Flash 9.0.115.0 and all Flash 10 versions, permissions for creating sockets using Flash have changed.
+ Now an explicit permission (using xml-syntax) is required for creating socket connections.
+ In the current state, socket server won't work with the newest Flash versions.
+ You will get a "Flash security error" in the browser. +

+

+ A solution is to use a policy-files server which will listen to connections in port 843 in the server.
+ Each time a client tries to connect to the chat, the Flash client will request the policy authorization to the server.
+ The policy-files server is downloadable from http://ammonlauritzen.com/FlashPolicyService-09b.zip
+ It works with FF3 and IE7 (not yet tested in other browsers). +

+

A more detailed explanation can be found here:

+

* http://ammonlauritzen.com/blog/2007/12/13/new-flash-security-policies/
+ * http://ammonlauritzen.com/blog/2008/04/22/flash-policy-service-daemon/
+

+

Official Adobe documentation:

+

* http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security.html
+ * http://www.adobe.com/devnet/flashplayer/articles/fplayer9_security_04.html

+
+

 

+
+ +

7. Support

+
+

+ Please do not email the devs with support questions.
+ For further documentation and some examples, check out our github wiki.
+ For general support questions use our google group.
+ For specific bug reports and a list of pending issues view our github project.
+

+
+ + +
+

+ Your donations contribute to the growth and development of this project and are always appreciated.
+

+ + + + +
+ I'm on gittip at https://www.gittip.com/Frug +

+
+ +

9. License

+
+

Bluimp's AJAX Chat is released under a Modified MIT License.

+

You should also find this license included with your download of this project.

+
+ +

back to top

+ +
+ \ No newline at end of file diff --git a/chat/sounds/sound_7.mp3 b/chat/sounds/sound_7.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..7ee9273e4001c30e5af30c687a6b7d97fa29e299 GIT binary patch literal 24241 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