2
* # Semantic - Chatroom
3
* http://github.com/jlukic/semantic-ui/
6
* Copyright 2014 Contributors
7
* Released under the MIT license
8
* http://opensource.org/licenses/MIT
12
;(function ($, window, document, undefined) {
14
$.fn.chatroom = function(parameters) {
16
$allModules = $(this),
17
moduleSelector = $allModules.selector || '',
19
time = new Date().getTime(),
23
methodInvoked = (typeof query == 'string'),
24
queryArguments = [].slice.call(arguments, 1),
30
settings = $.extend(true, {}, $.fn.chatroom.settings, parameters),
32
className = settings.className,
33
namespace = settings.namespace,
34
selector = settings.selector,
35
error = settings.error,
39
$expandButton = $module.find(selector.expandButton),
40
$userListButton = $module.find(selector.userListButton),
42
$userList = $module.find(selector.userList),
43
$room = $module.find(selector.room),
44
$userCount = $module.find(selector.userCount),
46
$log = $module.find(selector.log),
47
$message = $module.find(selector.message),
49
$messageInput = $module.find(selector.messageInput),
50
$messageButton = $module.find(selector.messageButton),
52
instance = $module.data('module'),
74
userList : $userList.outerWidth()
77
initialize: function() {
79
// check error conditions
80
if(Pusher === undefined) {
81
module.error(error.pusher);
83
if(settings.key === undefined || settings.channelName === undefined) {
84
module.error(error.key);
87
else if( !(settings.endpoint.message || settings.endpoint.authentication) ) {
88
module.error(error.endpoint);
93
pusher = new Pusher(settings.key);
94
Pusher.channel_auth_endpoint = settings.endpoint.authentication;
96
channel = pusher.subscribe(settings.channelName);
98
channel.bind('pusher:subscription_succeeded', module.user.list.create);
99
channel.bind('pusher:subscription_error', module.error);
100
channel.bind('pusher:member_added', module.user.joined);
101
channel.bind('pusher:member_removed', module.user.left);
102
channel.bind('update_messages', module.message.receive);
104
$.each(settings.customEvents, function(label, value) {
105
channel.bind(label, value);
108
// bind module events
110
.on('click.' + namespace, module.event.toggleUserList)
113
.on('click.' + namespace, module.event.toggleExpand)
116
.on('keydown.' + namespace, module.event.input.keydown)
117
.on('keyup.' + namespace, module.event.input.keyup)
120
.on('mouseenter.' + namespace, module.event.hover)
121
.on('mouseleave.' + namespace, module.event.hover)
122
.on('click.' + namespace, module.event.submit)
124
// scroll to bottom of chat log
127
scrollTop: $log.prop('scrollHeight')
131
.data('module', module)
132
.addClass(className.loading)
138
refresh: function() {
139
// reset width calculations
141
.removeClass(className.active)
145
userList : $userList.outerWidth()
147
if( $userListButton.hasClass(className.active) ) {
148
module.user.list.hide();
150
$module.data('module', module);
155
updateCount: function() {
156
if(settings.userCount) {
157
users = $module.data('users');
159
$.each(users, function() {
163
.html( settings.templates.userCount(count) )
168
// add user to user list
169
joined: function(member) {
170
users = $module.data('users');
171
if(member.id != 'anonymous' && users[ member.id ] === undefined ) {
172
users[ member.id ] = member.info;
173
if(settings.randomColor && member.info.color === undefined) {
174
member.info.color = settings.templates.color(member.id);
176
html = settings.templates.userList(member.info);
177
if(member.info.isAdmin) {
179
.prependTo($userList)
187
if(settings.partingMessages) {
189
.append( settings.templates.joined(member.info) )
191
module.message.scroll.test();
193
module.user.updateCount();
197
// remove user from user list
198
left: function(member) {
199
users = $module.data('users');
200
if(member !== undefined && member.id !== 'anonymous') {
201
delete users[ member.id ];
203
.data('users', users)
206
.find('[data-id='+ member.id + ']')
209
if(settings.partingMessages) {
211
.append( settings.templates.left(member.info) )
213
module.message.scroll.test();
215
module.user.updateCount();
221
// receives list of members and generates user list
222
create: function(members) {
224
members.each(function(member) {
225
if(member.id !== 'anonymous' && member.id !== 'undefined') {
226
if(settings.randomColor && member.info.color === undefined) {
227
member.info.color = settings.templates.color(member.id);
229
// sort list with admin first
230
html = (member.info.isAdmin)
231
? settings.templates.userList(member.info) + html
232
: html + settings.templates.userList(member.info)
234
users[ member.id ] = member.info;
238
.data('users', users)
239
.data('user', users[members.me.id] )
240
.removeClass(className.loading)
245
module.user.updateCount();
246
$.proxy(settings.onJoin, $userList.children())();
253
width: (module.width.log - module.width.userList)
255
duration : settings.speed,
256
easing : settings.easing,
257
complete : module.message.scroll.move
267
width: (module.width.log)
269
duration : settings.speed,
270
easing : settings.easing,
271
complete : module.message.scroll.move
282
// handles scrolling of chat log
285
height = $log.prop('scrollHeight') - $log.height();
286
if( Math.abs($log.scrollTop() - height) < settings.scrollArea) {
287
module.message.scroll.move();
292
height = $log.prop('scrollHeight') - $log.height();
299
// sends chat message
300
send: function(message) {
301
if( !module.utils.emptyString(message) ) {
303
url : settings.endpoint.message,
308
timestamp : new Date().getTime()
315
// receives chat response and processes
316
receive: function(response) {
317
message = response.data;
318
users = $module.data('users');
319
loggedInUser = $module.data('user');
320
if(users[ message.userID] !== undefined) {
321
// logged in user's messages already pushed instantly
322
if(loggedInUser === undefined || loggedInUser.id != message.userID) {
323
message.user = users[ message.userID ];
324
module.message.display(message);
329
// displays message in chat log
330
display: function(message) {
332
.append( settings.templates.message(message) )
334
module.message.scroll.test();
335
$.proxy(settings.onMessage, $log.children().last() )();
342
.addClass(className.expand)
344
$.proxy(settings.onExpand, $module )();
348
contract: function() {
350
.removeClass(className.expand)
352
$.proxy(settings.onContract, $module )();
360
keydown: function(event) {
361
if(event.which == 13) {
363
.addClass(className.down)
368
keyup: function(event) {
369
if(event.which == 13) {
371
.removeClass(className.down)
373
module.event.submit();
379
// handles message form submit
382
message = $messageInput.val(),
383
loggedInUser = $module.data('user')
385
if(loggedInUser !== undefined && !module.utils.emptyString(message)) {
386
module.message.send(message);
387
// display immediately
388
module.message.display({
392
module.message.scroll.move();
400
// handles button click on expand button
401
toggleExpand: function() {
402
if( !$module.hasClass(className.expand) ) {
404
.addClass(className.active)
410
.removeClass(className.active)
416
// handles button click on user list button
417
toggleUserList: function() {
418
if( !$log.is(':animated') ) {
419
if( !$userListButton.hasClass(className.active) ) {
421
.addClass(className.active)
423
module.user.list.show();
427
.removeClass('active')
429
module.user.list.hide();
438
emptyString: function(string) {
439
if(typeof string == 'string') {
440
return (string.search(/\S/) == -1);
447
setting: function(name, value) {
448
if(value !== undefined) {
449
if( $.isPlainObject(name) ) {
450
$.extend(true, settings, name);
453
settings[name] = value;
457
return settings[name];
460
internal: function(name, value) {
461
if( $.isPlainObject(name) ) {
462
$.extend(true, module, name);
464
else if(value !== undefined) {
465
module[name] = value;
473
if(settings.performance) {
474
module.performance.log(arguments);
477
module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
478
module.debug.apply(console, arguments);
482
verbose: function() {
483
if(settings.verbose && settings.debug) {
484
if(settings.performance) {
485
module.performance.log(arguments);
488
module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
489
module.verbose.apply(console, arguments);
494
module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
495
module.error.apply(console, arguments);
498
log: function(message) {
504
if(settings.performance) {
505
currentTime = new Date().getTime();
506
previousTime = time || currentTime;
507
executionTime = currentTime - previousTime;
512
'Arguments' : [].slice.call(message, 1) || '',
513
'Execution Time' : executionTime
516
clearTimeout(module.performance.timer);
517
module.performance.timer = setTimeout(module.performance.display, 100);
519
display: function() {
521
title = settings.name + ':',
525
clearTimeout(module.performance.timer);
526
$.each(performance, function(index, data) {
527
totalTime += data['Execution Time'];
529
title += ' ' + totalTime + 'ms';
531
title += ' \'' + moduleSelector + '\'';
533
if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
534
console.groupCollapsed(title);
536
console.table(performance);
539
$.each(performance, function(index, data) {
540
console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
548
invoke: function(query, passedArguments, context) {
553
passedArguments = passedArguments || queryArguments;
554
context = element || context;
555
if(typeof query == 'string' && instance !== undefined) {
556
query = query.split(/[\. ]/);
557
maxDepth = query.length - 1;
558
$.each(query, function(depth, value) {
559
if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) {
560
instance = instance[value];
562
else if( instance[value] !== undefined ) {
563
found = instance[value];
566
module.error(error.method, query);
570
if ( $.isFunction( found ) ) {
571
return found.apply(context, passedArguments);
573
return found || false;
578
if(instance === undefined) {
581
module.invoke(query);
584
if(instance !== undefined) {
591
return (returnedValue !== undefined)
597
$.fn.chatroom.settings = {
603
channel : 'present-chat',
605
onJoin : function(){},
606
onMessage : function(){},
607
onExpand : function(){},
608
onContract : function(){},
612
partingMessages : false,
617
easing : 'easeOutQuint',
619
// pixels from bottom of chat log that should trigger auto scroll to bottom
624
authentication : false
628
method : 'The method you called is not defined',
629
endpoint : 'Please define a message and authentication endpoint.',
630
key : 'You must specify a pusher key and channel.',
631
pusher : 'You must include the Pusher library.'
643
userCount : '.actions .message',
644
userListButton : '.actions .list.button',
645
expandButton : '.actions .expand.button',
647
userList : '.room .list',
649
message : '.room .log .message',
650
author : '.room log .message .author',
651
messageInput : '.talk input',
652
messageButton : '.talk .send.button'
657
userCount: function(number) {
658
return number + ' users in chat';
661
color: function(userID) {
696
return colors[ Math.floor( Math.random() * colors.length) ];
699
message: function(message) {
703
if(message.user.isAdmin) {
704
message.user.color = '#55356A';
705
html += '<div class="admin message">';
706
html += '<span class="quirky ui flag team"></span>';
709
else if(message.user.isPro) {
710
html += '<div class="indent message">';
711
html += '<span class="quirky ui flag pro"></span>';
715
html += '<div class="message">';
718
if(message.user.color !== undefined) {
719
html += '<span class="author" style="color: ' + message.user.color + ';">' + message.user.name + '</span>: ';
722
html += '<span class="author">' + message.user.name + '</span>: ';
732
joined: function(member) {
733
return (typeof member.name !== undefined)
734
? '<div class="status">' + member.name + ' has joined the chat.</div>'
738
left: function(member) {
739
return (typeof member.name !== undefined)
740
? '<div class="status">' + member.name + ' has left the chat.</div>'
745
userList: function(member) {
750
member.color = '#55356A';
753
+ '<div class="user" data-id="' + member.id + '">'
754
+ ' <div class="image">'
755
+ ' <img src="' + member.avatarURL + '">'
758
if(member.color !== undefined) {
759
html += ' <p><a href="/users/' + member.id + '" target="_blank" style="color: ' + member.color + ';">' + member.name + '</a></p>';
762
html += ' <p><a href="/users/' + member.id + '" target="_blank">' + member.name + '</a></p>';
772
})( jQuery, window , document );