2011-08-11 16:26:41 +02:00
/ * *
* Copyright 2009 Google Inc . , 2011 Peter 'Pita' Martischka ( Primary Technology Ltd )
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS-IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
2012-03-07 02:27:03 +01:00
var padutils = require ( './pad_utils' ) . padutils ;
var padcookie = require ( './pad_cookie' ) . padcookie ;
2012-09-09 23:55:43 +02:00
var Tinycon = require ( 'tinycon/tinycon' ) ;
2013-03-19 03:21:53 +01:00
var hooks = require ( './pluginfw/hooks' ) ;
2015-03-26 17:58:13 +01:00
var padeditor = require ( './pad_editor' ) . padeditor ;
2012-07-12 20:18:33 +02:00
2011-07-14 17:15:38 +02:00
var chat = ( function ( )
{
2012-01-25 20:03:25 +01:00
var isStuck = false ;
2015-02-09 20:11:35 +01:00
var userAndChat = false ;
2013-01-06 16:11:48 +01:00
var gotInitialMessages = false ;
2013-01-07 17:36:03 +01:00
var historyPointer = 0 ;
2011-11-27 21:39:47 +01:00
var chatMentions = 0 ;
2011-07-14 17:15:38 +02:00
var self = {
2019-04-16 00:34:29 +02:00
show : function ( )
{
2020-04-08 18:14:20 +02:00
$ ( "#chaticon" ) . removeClass ( 'visible' ) ;
$ ( "#chatbox" ) . addClass ( 'visible' ) ;
2020-04-18 11:19:50 +02:00
self . scrollDown ( true ) ;
2011-11-27 21:39:47 +01:00
chatMentions = 0 ;
2012-07-12 20:18:33 +02:00
Tinycon . setBubble ( 0 ) ;
2020-04-18 11:19:50 +02:00
$ ( '.chat-gritter-msg' ) . each ( function ( ) {
$ . gritter . remove ( this . id ) ;
} ) ;
2011-07-14 17:15:38 +02:00
} ,
2019-04-16 00:34:29 +02:00
focus : function ( )
2015-03-26 17:44:22 +01:00
{
2015-03-26 17:58:13 +01:00
setTimeout ( function ( ) {
$ ( "#chatinput" ) . focus ( ) ;
} , 100 ) ;
2015-03-26 17:44:22 +01:00
} ,
2012-01-28 23:24:14 +01:00
stickToScreen : function ( fromInitialCall ) // Make chat stick to right hand side of screen
2012-01-25 20:03:25 +01:00
{
chat . show ( ) ;
2020-04-02 14:36:49 +02:00
isStuck = ( ! isStuck || fromInitialCall ) ;
2020-04-08 18:14:20 +02:00
$ ( '#chatbox' ) . hide ( ) ;
// Add timeout to disable the chatbox animations
setTimeout ( function ( ) {
$ ( '#chatbox, .sticky-container' ) . toggleClass ( "stickyChat" , isStuck ) ;
$ ( '#chatbox' ) . css ( 'display' , 'flex' ) ;
} , 0 ) ;
2020-04-02 14:36:49 +02:00
padcookie . setPref ( "chatAlwaysVisible" , isStuck ) ;
$ ( '#options-stickychat' ) . prop ( 'checked' , isStuck ) ;
2012-01-26 17:22:44 +01:00
} ,
2015-01-21 17:08:54 +01:00
chatAndUsers : function ( fromInitialCall )
{
2015-02-09 19:01:45 +01:00
var toEnable = $ ( '#options-chatandusers' ) . is ( ":checked" ) ;
2015-02-09 20:11:35 +01:00
if ( toEnable || ! userAndChat || fromInitialCall ) {
2015-01-19 02:45:49 +01:00
chat . stickToScreen ( true ) ;
2015-01-21 17:08:54 +01:00
$ ( '#options-stickychat' ) . prop ( 'checked' , true )
2015-02-09 20:11:35 +01:00
$ ( '#options-chatandusers' ) . prop ( 'checked' , true )
2015-01-19 02:45:49 +01:00
$ ( '#options-stickychat' ) . prop ( "disabled" , "disabled" ) ;
2015-02-09 20:11:35 +01:00
userAndChat = true ;
2015-01-19 02:45:49 +01:00
} else {
$ ( '#options-stickychat' ) . prop ( "disabled" , false ) ;
2020-04-02 14:36:49 +02:00
userAndChat = false ;
2015-01-19 02:45:49 +01:00
}
2020-04-02 14:36:49 +02:00
padcookie . setPref ( "chatAndUsers" , userAndChat ) ;
2020-04-06 19:30:51 +02:00
$ ( '#users, .sticky-container' ) . toggleClass ( "chatAndUsers popup-show stickyUsers" , userAndChat ) ;
2020-04-02 14:36:49 +02:00
$ ( "#chatbox" ) . toggleClass ( "chatAndUsersChat" , userAndChat ) ;
2015-01-19 02:45:49 +01:00
} ,
2019-04-16 00:34:29 +02:00
hide : function ( )
2011-07-14 17:15:38 +02:00
{
2019-04-16 00:34:29 +02:00
// decide on hide logic based on chat window being maximized or not
2013-12-15 11:02:43 +01:00
if ( $ ( '#options-stickychat' ) . prop ( 'checked' ) ) {
chat . stickToScreen ( ) ;
$ ( '#options-stickychat' ) . prop ( 'checked' , false ) ;
}
2019-04-16 00:34:29 +02:00
else {
2013-12-15 11:02:43 +01:00
$ ( "#chatcounter" ) . text ( "0" ) ;
2020-04-08 18:14:20 +02:00
$ ( "#chaticon" ) . addClass ( 'visible' ) ;
$ ( "#chatbox" ) . removeClass ( 'visible' ) ;
2013-12-15 11:02:43 +01:00
}
2011-07-14 17:15:38 +02:00
} ,
2020-04-18 11:19:50 +02:00
scrollDown : function ( force )
2011-07-14 17:15:38 +02:00
{
2020-04-18 11:19:50 +02:00
if ( $ ( '#chatbox' ) . hasClass ( 'visible' ) ) {
if ( force || ! self . lastMessage || ! self . lastMessage . position ( ) || self . lastMessage . position ( ) . top < ( $ ( '#chattext' ) . outerHeight ( ) + 20 ) ) {
2015-02-08 00:23:33 +01:00
// if we use a slow animate here we can have a race condition when a users focus can not be moved away
// from the last message recieved.
2015-02-08 15:34:48 +01:00
$ ( '#chattext' ) . animate ( { scrollTop : $ ( '#chattext' ) [ 0 ] . scrollHeight } , { duration : 400 , queue : false } ) ;
2012-07-11 22:30:03 +02:00
self . lastMessage = $ ( '#chattext > p' ) . eq ( - 1 ) ;
2012-07-11 18:42:59 +02:00
}
}
2019-04-16 00:34:29 +02:00
} ,
2011-07-14 17:15:38 +02:00
send : function ( )
{
var text = $ ( "#chatinput" ) . val ( ) ;
2013-01-16 18:48:25 +01:00
if ( text . replace ( /\s+/ , '' ) . length == 0 )
2013-01-14 17:11:56 +01:00
return ;
2012-01-16 06:05:19 +01:00
this . _pad . collabClient . sendMessage ( { "type" : "CHAT_MESSAGE" , "text" : text } ) ;
2011-07-14 17:15:38 +02:00
$ ( "#chatinput" ) . val ( "" ) ;
} ,
2013-01-06 16:11:48 +01:00
addMessage : function ( msg , increment , isHistoryAdd )
2013-03-19 20:21:27 +01:00
{
2011-07-14 17:15:38 +02:00
//correct the time
2012-01-16 06:05:19 +01:00
msg . time += this . _pad . clientTimeOffset ;
2019-04-16 00:34:29 +02:00
2011-07-14 17:15:38 +02:00
//create the time string
var minutes = "" + new Date ( msg . time ) . getMinutes ( ) ;
var hours = "" + new Date ( msg . time ) . getHours ( ) ;
if ( minutes . length == 1 )
minutes = "0" + minutes ;
if ( hours . length == 1 )
hours = "0" + hours ;
var timeStr = hours + ":" + minutes ;
2019-04-16 00:34:29 +02:00
2011-07-14 17:15:38 +02:00
//create the authorclass
2020-03-29 20:41:28 +02:00
if ( ! msg . userId ) {
/ *
* If , for a bug or a database corruption , the message coming from the
* server does not contain the userId field ( see for example # 3731 ) ,
* let ' s be defensive and replace it with "unknown" .
* /
msg . userId = "unknown" ;
console . warn ( 'The "userId" field of a chat message coming from the server was not present. Replacing with "unknown". This may be a bug or a database corruption.' ) ;
}
2011-07-14 17:15:38 +02:00
var authorClass = "author-" + msg . userId . replace ( /[^a-y0-9]/g , function ( c )
{
if ( c == "." ) return "-" ;
return 'z' + c . charCodeAt ( 0 ) + 'z' ;
} ) ;
2012-02-20 02:11:34 +01:00
var text = padutils . escapeHtmlWithClickableLinks ( msg . text , "_blank" ) ;
2011-11-27 17:35:20 +01:00
2013-03-19 20:21:27 +01:00
var authorName = msg . userName == null ? _ ( 'pad.userlist.unnamed' ) : padutils . escapeHtml ( msg . userName ) ;
// the hook args
var ctx = {
"authorName" : authorName ,
"author" : msg . userId ,
"text" : text ,
"sticky" : false ,
"timestamp" : msg . time ,
2016-10-08 03:14:23 +02:00
"timeStr" : timeStr ,
"duration" : 4000
2011-11-27 17:35:20 +01:00
}
2013-03-19 20:21:27 +01:00
// is the users focus already in the chatbox?
var alreadyFocused = $ ( "#chatinput" ) . is ( ":focus" ) ;
2013-01-29 02:55:36 +01:00
2013-03-19 20:21:27 +01:00
// does the user already have the chatbox open?
2020-04-18 11:19:50 +02:00
var chatOpen = $ ( "#chatbox" ) . hasClass ( "visible" ) ;
2013-01-29 02:55:36 +01:00
2013-03-19 20:21:27 +01:00
// does this message contain this user's name? (is the curretn user mentioned?)
var myName = $ ( '#myusernameedit' ) . val ( ) ;
var wasMentioned = ( text . toLowerCase ( ) . indexOf ( myName . toLowerCase ( ) ) !== - 1 && myName != "undefined" ) ;
if ( wasMentioned && ! alreadyFocused && ! isHistoryAdd && ! chatOpen )
2020-04-18 11:19:50 +02:00
{ // If the user was mentioned, make the message sticky
2013-03-19 20:21:27 +01:00
chatMentions ++ ;
Tinycon . setBubble ( chatMentions ) ;
ctx . sticky = true ;
}
// Call chat message hook
hooks . aCallAll ( "chatNewMessage" , ctx , function ( ) {
var html = "<p data-authorId='" + msg . userId + "' class='" + authorClass + "'><b>" + authorName + ":</b><span class='time " + authorClass + "'>" + ctx . timeStr + "</span> " + ctx . text + "</p>" ;
if ( isHistoryAdd )
$ ( html ) . insertAfter ( '#chatloadmessagesbutton' ) ;
2011-11-27 17:35:20 +01:00
else
2013-03-19 20:21:27 +01:00
$ ( "#chattext" ) . append ( html ) ;
//should we increment the counter??
if ( increment && ! isHistoryAdd )
2011-11-27 17:35:20 +01:00
{
2013-03-19 20:21:27 +01:00
// Update the counter of unread messages
var count = Number ( $ ( "#chatcounter" ) . text ( ) ) ;
count ++ ;
$ ( "#chatcounter" ) . text ( count ) ;
2016-10-08 03:14:23 +02:00
if ( ! chatOpen && ctx . duration > 0 ) {
2013-01-29 02:55:36 +01:00
$ . gritter . add ( {
2020-04-18 11:19:50 +02:00
text : '<span class="author-name">' + ctx . authorName + '</span>' + ctx . text ,
2013-03-19 20:21:27 +01:00
sticky : ctx . sticky ,
2020-04-18 11:19:50 +02:00
time : 5000 ,
position : 'bottom' ,
class _name : 'chat-gritter-msg'
2013-01-29 02:55:36 +01:00
} ) ;
}
2011-11-27 17:35:20 +01:00
}
2013-03-19 20:21:27 +01:00
} ) ;
// Clear the chat mentions when the user clicks on the chat input box
2012-06-12 22:52:22 +02:00
$ ( '#chatinput' ) . click ( function ( ) {
chatMentions = 0 ;
2012-07-12 20:18:33 +02:00
Tinycon . setBubble ( 0 ) ;
2012-06-12 22:52:22 +02:00
} ) ;
2013-01-06 16:11:48 +01:00
if ( ! isHistoryAdd )
self . scrollDown ( ) ;
2011-07-14 17:15:38 +02:00
} ,
2012-01-16 06:05:19 +01:00
init : function ( pad )
2011-07-14 17:15:38 +02:00
{
2012-01-16 06:05:19 +01:00
this . _pad = pad ;
2015-05-06 01:32:36 +02:00
$ ( "#chatinput" ) . on ( "keydown" , function ( evt ) {
2015-03-26 17:58:13 +01:00
// If the event is Alt C or Escape & we're already in the chat menu
// Send the users focus back to the pad
if ( ( evt . altKey == true && evt . which === 67 ) || evt . which === 27 ) {
// If we're in chat already..
$ ( ':focus' ) . blur ( ) ; // required to do not try to remove!
padeditor . ace . focus ( ) ; // Sends focus back to pad
2015-05-06 01:32:36 +02:00
evt . preventDefault ( ) ;
return false ;
2015-03-26 17:58:13 +01:00
}
} ) ;
2015-05-06 01:32:36 +02:00
$ ( 'body:not(#chatinput)' ) . on ( "keypress" , function ( evt ) {
2015-03-31 14:45:11 +02:00
if ( evt . altKey && evt . which == 67 ) {
// Alt c focuses on the Chat window
$ ( this ) . blur ( ) ;
2015-05-06 01:32:36 +02:00
chat . show ( ) ;
$ ( "#chatinput" ) . focus ( ) ;
2015-03-31 14:45:11 +02:00
evt . preventDefault ( ) ;
}
} ) ;
2015-03-26 17:58:13 +01:00
$ ( "#chatinput" ) . keypress ( function ( evt ) {
2011-07-14 17:15:38 +02:00
//if the user typed enter, fire the send
2012-10-28 18:38:56 +01:00
if ( evt . which == 13 || evt . which == 10 )
2011-07-14 17:15:38 +02:00
{
evt . preventDefault ( ) ;
self . send ( ) ;
}
} ) ;
2013-12-05 08:41:29 +01:00
// initial messages are loaded in pad.js' _afterHandshake
$ ( "#chatcounter" ) . text ( 0 ) ;
$ ( "#chatloadmessagesbutton" ) . click ( function ( )
{
2013-01-07 17:43:03 +01:00
var start = Math . max ( self . historyPointer - 20 , 0 ) ;
var end = self . historyPointer ;
2013-01-07 17:36:03 +01:00
if ( start == end ) // nothing to load
return ;
2013-01-07 19:15:55 +01:00
$ ( "#chatloadmessagesbutton" ) . css ( "display" , "none" ) ;
$ ( "#chatloadmessagesball" ) . css ( "display" , "block" ) ;
2013-01-07 17:36:03 +01:00
pad . collabClient . sendMessage ( { "type" : "GET_CHAT_MESSAGES" , "start" : start , "end" : end } ) ;
self . historyPointer = start ;
2013-12-05 08:41:29 +01:00
} ) ;
2011-07-14 17:15:38 +02:00
}
}
return self ;
2011-12-04 16:33:56 +01:00
} ( ) ) ;
2012-01-16 02:23:48 +01:00
exports . chat = chat ;
2012-01-26 17:22:44 +01:00