|
|
/* * # Semantic - Chatroom * http://github.com/jlukic/semantic-ui/
* * * Copyright 2013 Contributors * Released under the MIT license * http://opensource.org/licenses/MIT
* */
;(function ($, window, document, undefined) {
$.fn.chatroom = function(parameters) { var $allModules = $(this), moduleSelector = $allModules.selector || '',
time = new Date().getTime(), performance = [],
query = arguments[0], methodInvoked = (typeof query == 'string'), queryArguments = [].slice.call(arguments, 1), returnedValue ; $(this) .each(function() { var settings = $.extend(true, {}, $.fn.chatroom.settings, parameters),
className = settings.className, namespace = settings.namespace, selector = settings.selector, error = settings.error,
$module = $(this),
$expandButton = $module.find(selector.expandButton), $userListButton = $module.find(selector.userListButton),
$userList = $module.find(selector.userList), $room = $module.find(selector.room), $userCount = $module.find(selector.userCount),
$log = $module.find(selector.log), $message = $module.find(selector.message),
$messageInput = $module.find(selector.messageInput), $messageButton = $module.find(selector.messageButton),
instance = $module.data('module'), element = this,
html = '', users = {},
channel, loggedInUser,
message, count,
height,
pusher, module ;
module = {
width: { log : $log.width(), userList : $userList.outerWidth() },
initialize: function() {
// check error conditions
if(Pusher === undefined) { module.error(error.pusher); } if(settings.key === undefined || settings.channelName === undefined) { module.error(error.key); return false; } else if( !(settings.endpoint.message || settings.endpoint.authentication) ) { module.error(error.endpoint); return false; }
// define pusher
pusher = new Pusher(settings.key); Pusher.channel_auth_endpoint = settings.endpoint.authentication;
channel = pusher.subscribe(settings.channelName);
channel.bind('pusher:subscription_succeeded', module.user.list.create); channel.bind('pusher:subscription_error', module.error); channel.bind('pusher:member_added', module.user.joined); channel.bind('pusher:member_removed', module.user.left); channel.bind('update_messages', module.message.receive);
$.each(settings.customEvents, function(label, value) { channel.bind(label, value); });
// bind module events
$userListButton .on('click.' + namespace, module.event.toggleUserList) ; $expandButton .on('click.' + namespace, module.event.toggleExpand) ; $messageInput .on('keydown.' + namespace, module.event.input.keydown) .on('keyup.' + namespace, module.event.input.keyup) ; $messageButton .on('mouseenter.' + namespace, module.event.hover) .on('mouseleave.' + namespace, module.event.hover) .on('click.' + namespace, module.event.submit) ; // scroll to bottom of chat log
$log .animate({ scrollTop: $log.prop('scrollHeight') }, 400) ; $module .data('module', module) .addClass(className.loading) ;
},
// refresh module
refresh: function() { // reset width calculations
$userListButton .removeClass(className.active) ; module.width = { log : $log.width(), userList : $userList.outerWidth() }; if( $userListButton.hasClass(className.active) ) { module.user.list.hide(); } $module.data('module', module); },
user: {
updateCount: function() { if(settings.userCount) { users = $module.data('users'); count = 0; $.each(users, function() { count++; }); $userCount .html( settings.templates.userCount(count) ) ; } },
// add user to user list
joined: function(member) { users = $module.data('users'); if(member.id != 'anonymous' && users[ member.id ] === undefined ) { users[ member.id ] = member.info; if(settings.randomColor && member.info.color === undefined) { member.info.color = settings.templates.color(member.id); } html = settings.templates.userList(member.info); if(member.info.isAdmin) { $(html) .prependTo($userList) ; } else { $(html) .appendTo($userList) ; } if(settings.partingMessages) { $log .append( settings.templates.joined(member.info) ) ; module.message.scroll.test(); } module.user.updateCount(); } },
// remove user from user list
left: function(member) { users = $module.data('users'); if(member !== undefined && member.id !== 'anonymous') { delete users[ member.id ]; $module .data('users', users) ; $userList .find('[data-id='+ member.id + ']') .remove() ; if(settings.partingMessages) { $log .append( settings.templates.left(member.info) ) ; module.message.scroll.test(); } module.user.updateCount(); } },
list: {
// receives list of members and generates user list
create: function(members) { users = {}; members.each(function(member) { if(member.id !== 'anonymous' && member.id !== 'undefined') { if(settings.randomColor && member.info.color === undefined) { member.info.color = settings.templates.color(member.id); } // sort list with admin first
html = (member.info.isAdmin) ? settings.templates.userList(member.info) + html : html + settings.templates.userList(member.info) ; users[ member.id ] = member.info; } }); $module .data('users', users) .data('user', users[members.me.id] ) .removeClass(className.loading) ; $userList .html(html) ; module.user.updateCount(); $.proxy(settings.onJoin, $userList.children())(); },
// shows user list
show: function() { $log .animate({ width: (module.width.log - module.width.userList) }, { duration : settings.speed, easing : settings.easing, complete : module.message.scroll.move }) ; },
// hides user list
hide: function() { $log .stop() .animate({ width: (module.width.log) }, { duration : settings.speed, easing : settings.easing, complete : module.message.scroll.move }) ; }
}
},
message: {
// handles scrolling of chat log
scroll: { test: function() { height = $log.prop('scrollHeight') - $log.height(); if( Math.abs($log.scrollTop() - height) < settings.scrollArea) { module.message.scroll.move(); } },
move: function() { height = $log.prop('scrollHeight') - $log.height(); $log .scrollTop(height) ; } },
// sends chat message
send: function(message) { if( !module.utils.emptyString(message) ) { $.api({ url : settings.endpoint.message, method : 'POST', data : { 'message': { content : message, timestamp : new Date().getTime() } } }); } },
// receives chat response and processes
receive: function(response) { message = response.data; users = $module.data('users'); loggedInUser = $module.data('user'); if(users[ message.userID] !== undefined) { // logged in user's messages already pushed instantly
if(loggedInUser === undefined || loggedInUser.id != message.userID) { message.user = users[ message.userID ]; module.message.display(message); } } },
// displays message in chat log
display: function(message) { $log .append( settings.templates.message(message) ) ; module.message.scroll.test(); $.proxy(settings.onMessage, $log.children().last() )(); }
},
expand: function() { $module .addClass(className.expand) ; $.proxy(settings.onExpand, $module )(); module.refresh(); },
contract: function() { $module .removeClass(className.expand) ; $.proxy(settings.onContract, $module )(); module.refresh(); },
event: {
input: {
keydown: function(event) { if(event.which == 13) { $messageButton .addClass(className.down) ; } },
keyup: function(event) { if(event.which == 13) { $messageButton .removeClass(className.down) ; module.event.submit(); } }
},
// handles message form submit
submit: function() { var message = $messageInput.val(), loggedInUser = $module.data('user') ; if(loggedInUser !== undefined && !module.utils.emptyString(message)) { module.message.send(message); // display immediately
module.message.display({ user: loggedInUser, text: message }); module.message.scroll.move(); $messageInput .val('') ;
} },
// handles button click on expand button
toggleExpand: function() { if( !$module.hasClass(className.expand) ) { $expandButton .addClass(className.active) ; module.expand(); } else { $expandButton .removeClass(className.active) ; module.contract(); } },
// handles button click on user list button
toggleUserList: function() { if( !$log.is(':animated') ) { if( !$userListButton.hasClass(className.active) ) { $userListButton .addClass(className.active) ; module.user.list.show(); } else { $userListButton .removeClass('active') ; module.user.list.hide(); } }
} },
utils: {
emptyString: function(string) { if(typeof string == 'string') { return (string.search(/\S/) == -1); } return false; }
},
setting: function(name, value) { if(value !== undefined) { if( $.isPlainObject(name) ) { $.extend(true, settings, name); } else { settings[name] = value; } } else { return settings[name]; } }, internal: function(name, value) { if( $.isPlainObject(name) ) { $.extend(true, module, name); } else if(value !== undefined) { module[name] = value; } else { return module[name]; } }, debug: function() { if(settings.debug) { if(settings.performance) { module.performance.log(arguments); } else { module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.debug.apply(console, arguments); } } }, verbose: function() { if(settings.verbose && settings.debug) { if(settings.performance) { module.performance.log(arguments); } else { module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); module.verbose.apply(console, arguments); } } }, error: function() { module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); module.error.apply(console, arguments); }, performance: { log: function(message) { var currentTime, executionTime, previousTime ; if(settings.performance) { currentTime = new Date().getTime(); previousTime = time || currentTime; executionTime = currentTime - previousTime; time = currentTime; performance.push({ 'Element' : element, 'Name' : message[0], 'Arguments' : [].slice.call(message, 1) || '', 'Execution Time' : executionTime }); } clearTimeout(module.performance.timer); module.performance.timer = setTimeout(module.performance.display, 100); }, display: function() { var title = settings.name + ':', totalTime = 0 ; time = false; clearTimeout(module.performance.timer); $.each(performance, function(index, data) { totalTime += data['Execution Time']; }); title += ' ' + totalTime + 'ms'; if(moduleSelector) { title += ' \'' + moduleSelector + '\''; } if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { console.groupCollapsed(title); if(console.table) { console.table(performance); } else { $.each(performance, function(index, data) { console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); }); } console.groupEnd(); } performance = []; } }, invoke: function(query, passedArguments, context) { var maxDepth, found ; passedArguments = passedArguments || queryArguments; context = element || context; if(typeof query == 'string' && instance !== undefined) { query = query.split(/[\. ]/); maxDepth = query.length - 1; $.each(query, function(depth, value) { if( $.isPlainObject( instance[value] ) && (depth != maxDepth) ) { instance = instance[value]; } else if( instance[value] !== undefined ) { found = instance[value]; } else { module.error(error.method, query); } }); } if ( $.isFunction( found ) ) { return found.apply(context, passedArguments); } return found || false; } };
if(methodInvoked) { if(instance === undefined) { module.initialize(); } module.invoke(query); } else { if(instance !== undefined) { module.destroy(); } module.initialize(); } }) ; return (returnedValue !== undefined) ? returnedValue : this ; };
$.fn.chatroom.settings = {
name : 'Chat', debug : false, namespace : 'chat',
channel : 'present-chat',
onJoin : function(){}, onMessage : function(){}, onExpand : function(){}, onContract : function(){},
customEvents : {},
partingMessages : false, userCount : true, randomColor : true,
speed : 300, easing : 'easeOutQuint',
// pixels from bottom of chat log that should trigger auto scroll to bottom
scrollArea : 9999,
endpoint : { message : false, authentication : false },
error: { method : 'The method you called is not defined', endpoint : 'Please define a message and authentication endpoint.', key : 'You must specify a pusher key and channel.', pusher : 'You must include the Pusher library.' },
className : { expand : 'expand', active : 'active', hover : 'hover', down : 'down', loading : 'loading' },
selector : { userCount : '.actions .message', userListButton : '.actions .list.button', expandButton : '.actions .expand.button', room : '.room', userList : '.room .list', log : '.room .log', message : '.room .log .message', author : '.room log .message .author', messageInput : '.talk input', messageButton : '.talk .send.button' },
templates: {
userCount: function(number) { return number + ' users in chat'; },
color: function(userID) { var colors = [ '#000000', '#333333', '#666666', '#999999', '#CC9999', '#CC6666', '#CC3333', '#993333', '#663333', '#CC6633', '#CC9966', '#CC9933', '#999966', '#CCCC66', '#99CC66', '#669933', '#669966', '#33A3CC', '#336633', '#33CCCC', '#339999', '#336666', '#336699', '#6666CC', '#9966CC', '#333399', '#663366', '#996699', '#993366', '#CC6699' ] ; return colors[ Math.floor( Math.random() * colors.length) ]; },
message: function(message) { var html = '' ; if(message.user.isAdmin) { message.user.color = '#55356A'; html += '<div class="admin message">'; html += '<span class="quirky ui flag team"></span>'; } /* else if(message.user.isPro) { html += '<div class="indent message">'; html += '<span class="quirky ui flag pro"></span>'; } */ else { html += '<div class="message">'; } html += '<p>'; if(message.user.color !== undefined) { html += '<span class="author" style="color: ' + message.user.color + ';">' + message.user.name + '</span>: '; } else { html += '<span class="author">' + message.user.name + '</span>: '; } html += '' + message.text + ' </p>' + '</div>' ; return html; },
joined: function(member) { return (typeof member.name !== undefined) ? '<div class="status">' + member.name + ' has joined the chat.</div>' : false ; }, left: function(member) { return (typeof member.name !== undefined) ? '<div class="status">' + member.name + ' has left the chat.</div>' : false ; },
userList: function(member) { var html = '' ; if(member.isAdmin) { member.color = '#55356A'; } html += '' + '<div class="user" data-id="' + member.id + '">' + ' <div class="image">' + ' <img src="' + member.avatarURL + '">' + ' </div>' ; if(member.color !== undefined) { html += ' <p><a href="/users/' + member.id + '" target="_blank" style="color: ' + member.color + ';">' + member.name + '</a></p>'; } else { html += ' <p><a href="/users/' + member.id + '" target="_blank">' + member.name + '</a></p>'; } html += '</div>'; return html; }
}
};
})( jQuery, window , document );
|