Created
December 11, 2009 15:44
-
-
Save cheeaun/254280 to your computer and use it in GitHub Desktop.
Private Chat plugin for Talkerapp, now with chat log storage.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// https://cheeaun.talkerapp.com/plugins/82 | |
var vendorStyles = function(str){ | |
var s = str.split('-'); | |
if (s.length == 4 && s[0] == 'border'){ | |
var value = s[3].split(':')[1]; | |
return str + '-webkit-' + str + '-moz-border-radius-' + s[1] + s[2] + ':' + value + ';'; | |
} | |
return str + '-moz-' + str + '-webkit-' + str; | |
}; | |
var $style = $('<style>' | |
+ '.talkerapp-private-chat-pane{' | |
+ 'position: fixed; bottom: 0; right: 10px; width: 240px; height: 20em; background-color: #fff; z-index: 100;' | |
+ vendorStyles('box-shadow: 0 0 10px #666;') | |
+ vendorStyles('border-top-left-radius: 3px;') | |
+ vendorStyles('border-top-right-radius: 3px;') | |
+ '}' | |
+ '.talkerapp-private-chat-pane.collapsed{' | |
+ 'height: 24px;' | |
+ '}' | |
+ '.talkerapp-private-chat-head{' | |
+ 'height: 24px; line-height: 24px; font-weight: bold; padding: 0 .5em; color: #fff; background-color: #4091BF; cursor: pointer;' | |
+ vendorStyles('border-top-left-radius: 3px;') | |
+ vendorStyles('border-top-right-radius: 3px;') | |
+ '}' | |
+ '.talkerapp-private-chat-pane.new-msg .talkerapp-private-chat-head{' | |
+ 'background-color: #EF9E2D;' | |
+ '}' | |
+ (window.localStorage ? | |
('.talkerapp-private-chat-pane .talkerapp-private-chat-head .log{' | |
+ 'width: 16px; height: 16px; text-align: center; line-height: 16px; position: absolute; top: 4px; right: 24px;' | |
+ 'color: #fff; text-decoration: none;' | |
+ vendorStyles('border-radius: 3px;') | |
+ '}' | |
+ '.talkerapp-private-chat-pane .talkerapp-private-chat-head .log:hover{' | |
+ 'background-color: rgba(0,0,0,.2)' | |
+ '}') : | |
'.talkerapp-private-chat-pane .talkerapp-private-chat-head .log{ display: none; }') | |
+ '.talkerapp-private-chat-pane .talkerapp-private-chat-head .close{' | |
+ 'width: 16px; height: 16px; text-align: center; line-height: 16px; position: absolute; top: 4px; right: 4px;' | |
+ 'color: #fff; text-decoration: none;' | |
+ vendorStyles('border-radius: 3px;') | |
+ '}' | |
+ '.talkerapp-private-chat-pane .talkerapp-private-chat-head .close:hover{' | |
+ 'background-color: rgba(0,0,0,.2)' | |
+ '}' | |
+ '.talkerapp-private-chat-body{' | |
+ 'position: absolute; top: 24px; left: 0; bottom: 0; right: 0; overflow: hidden;' | |
+ '}' | |
+ '.talkerapp-private-chat-body ol{' | |
+ 'margin: 0; padding: 0; list-style: none; position: absolute; top: 0; left: 0; bottom: 48px; width: 100%; overflow: auto;' | |
+ '}' | |
+ '.talkerapp-private-chat-body ol li{' | |
+ 'margin: 0; padding: 0 .5em; list-style: none; display: block; font-size: .9em; line-height: 1.5em; overflow: hidden;' | |
+ '}' | |
+ '.talkerapp-private-chat-body ol li .talker{' | |
+ 'font-weight: bold; float: left; margin-right: .5em;' | |
+ '}' | |
+ '.talkerapp-private-chat-body ol li .msg{' | |
+ 'display: block; padding-left: 1em;' | |
+ '}' | |
+ '.talkerapp-private-chat-body ol li.divider{' | |
+ 'padding: 0; line-height: 0; height: 0; border-top: 1px dotted #ccc; margin: 0 .5em;' | |
+ '}' | |
+ '.talkerapp-private-chat-msgbox{' | |
+ 'position: absolute; left: 0; bottom: 0; right: 0; overflow: hidden; height: 48px;' | |
+ '}' | |
+ '.talkerapp-private-chat-msgbox textarea{' | |
+ 'position: absolute; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; resize: none;' | |
+ '}' | |
+ '.message.me.private{display: none;}' | |
+ '</style>'); | |
$('head').append($style); | |
var chatItemHTML = '<li title="<%= talkTime %>">' | |
+ '<strong class="talker"><%= talkerNick %>:</strong>' | |
+ '<span class="msg"><%= talkerMsg %></span>' | |
+ '</li>'; | |
var chatItemHTML2 = '<li>' | |
+ '<span class="msg"><%= talkerMsg %></span>' | |
+ '</li>'; | |
var chatPaneHTML = '<div class="talkerapp-private-chat-pane" data-talker="<%= talkerID %>">' | |
+ '<div class="talkerapp-private-chat-head">' | |
+ '<%= talkerID %> ' | |
+ '<a href="#" class="log" title="log">=</a> ' | |
+ '<a href="#" class="close" title="close">×</a>' | |
+ '</div>' | |
+ '<div class="talkerapp-private-chat-body">' | |
+ '<ol class="talkerapp-private-chat-list">' | |
+ '</ol>' | |
+ '<div class="talkerapp-private-chat-msgbox">' | |
+ '<textarea></textarea>' | |
+ '</div>' | |
+ '</div>' | |
+ '</div>'; | |
var chatDividerHTML = '<li class="divider"></li>'; | |
var chatLogScript = 'function clearall(){' | |
+ 'if (localStorage["talkerapp:privatechat:<%= id %>"] && confirm("Are you sure to clear ALL this chat log?")){' | |
+ 'localStorage.removeItem("talkerapp:privatechat:<%= id %>");' | |
+ 'window.close();' | |
+ '} ' | |
+ '}'; | |
var chatLogHTML = '<!DOCTYPE html>' | |
+ '<title>Chat with <%= id %></title>' | |
+ '<meta charset="utf-8">' | |
+ '<link rel="stylesheet" href="http://cheeaun.github.com/cacss/ca.min.css">' | |
+ '<style>' | |
+ 'h1 {font-size: 1.5em;}' | |
+ '.clearall {position: absolute; top: 2em; right: 2em;}' | |
+ 'ol {margin: 0; list-style: none;}' | |
+ '.date {padding: .3em .75em; background-color: #eee; text-shadow: 0 1px #fff;}' | |
+ '.time {float: left; width: 6em; color: #999;}' | |
+ '.talker {float: left; margin-right: .5em;}' | |
+ '.msg {display: block; margin-left: 6em; padding-left: 1em;}' | |
+ '</style>' | |
+ '<script>' + chatLogScript + '</scr' + 'ipt>' | |
+ '<h1>Chat with <%= id %></h1>' | |
+ '<p class="clearall"><a href="#" onclick="clearall(); return false;">Clear all</a></p>' | |
+ '<ol><%= list %></ol>'; | |
var chatLogItemDate = '<li class="date"><%= date %></li>'; | |
var chatLogItem = '<li>' | |
+ '<span class="time"><%= time %></span> ' | |
+ '<strong class="talker"><%= talker %></strong>' | |
+ '<span class="msg"><%= msg %></span>' | |
+ '</li>'; | |
var chats = {}; | |
var chatLogging = false; | |
var talkers = []; | |
var me = Talker.currentUser.name; | |
var paneWidth = 240; | |
var paneSpace = 10; | |
var url_expression = /(https?:\/\/|www\.)[^\s<]*/gi; | |
var protocol_expression = /^(http|https|ftp|ftps|ssh|irc|mms|file|about|mailto|xmpp):\/\//; | |
var formatMsg = function(msg){ | |
if (!msg) return; | |
return msg.replace(/"/g, '"') | |
.replace(/</g, '<') | |
.replace(/>/g, '>') | |
.replace(url_expression, function(l){ | |
return '<a href="' | |
+ (!l.match(protocol_expression) ? 'http://' : '') + l | |
+ '" target="_blank">' + l + '</a>'; | |
}); | |
}; | |
var insertMsg = function(id, talker, msg){ | |
if (id == me) return; // don't talk to yourself, ok? | |
var inTalkers = ($.inArray(id, talkers) != -1); | |
var pos = talkers.length; | |
if (!inTalkers){ | |
talkers.push(id); | |
var html = _.template(chatPaneHTML, { | |
talkerID: id | |
}); | |
$('body').append(html); | |
} | |
var pane = $('.talkerapp-private-chat-pane[data-talker=' + id + ']'); | |
if (!inTalkers) pane.css('right', paneSpace+(paneSpace+paneWidth)*pos); | |
var collapsed = pane.hasClass('collapsed'); | |
if (collapsed) pane.addClass('new-msg'); | |
if (!msg){ | |
if (!collapsed) pane.find('.talkerapp-private-chat-msgbox textarea').focus(); | |
return; | |
} | |
var lastTalker = pane.data('lastTalker'); | |
var datetime = new Date(); | |
var time = datetime.toLocaleTimeString(); | |
var html = _.template((lastTalker != talker) ? chatItemHTML : chatItemHTML2, { | |
talkTime: time, | |
talkerNick: talker, | |
talkerMsg: formatMsg(msg) | |
}); | |
pane.find('ol').append(html); | |
pane.data('lastTalker', talker); | |
// scroll to bottom | |
var body = pane.find('.talkerapp-private-chat-body ol')[0]; | |
body.scrollTop = body.scrollHeight; | |
if (window.localStorage){ | |
if (!chats[id]){ | |
var c = localStorage['talkerapp:privatechat:' + id]; | |
chats[id] = (c) ? JSON.decode(c) : []; | |
} | |
chats[id].push({ | |
datetime: +datetime, | |
talker: talker, | |
msg: msg | |
}); | |
if (!chatLogging) setTimeout(function(){ | |
var json = JSON.encode(chats[id]); | |
localStorage['talkerapp:privatechat:' + id] = json; | |
chatLogging = false; | |
}, 5000); | |
} | |
}; | |
// Received private messages | |
plugin.onMessageReceived = function(e){ | |
if (!e || !e.private || e.type != 'message') return; | |
var talker = e.user.name; | |
var msg = e.content; | |
insertMsg(talker, talker, msg); | |
return false; | |
}; | |
// Send private messages | |
plugin.onMessageSent = function(e){ | |
if (!e || e.type != 'command' || e.command != 'msg' || !e.args || e.args.length <= 1) return; | |
var args = e.args.slice(); | |
var talker = args.shift().replace(/^@/, ''); | |
var msg = args.join(' '); | |
insertMsg(talker, me, msg); | |
return false; | |
} | |
var offline = false; | |
var disableTextareas = function(){ | |
var users = _.map(Talker.getRoomUsers(), function(user){ | |
return user.name; | |
}); | |
var offUsers = []; | |
for (var i=0, l=talkers.length; i<l; i++){ | |
if ($.inArray(talkers[i], users) == -1) offUsers.push(talkers[i]); | |
} | |
var panes = $('.talkerapp-private-chat-pane'); | |
panes.css('opacity', 1).find('textarea').removeAttr('disabled'); | |
for (var j=0, l=offUsers.length; j<l; j++){ | |
panes.filter('[data-talker=' + offUsers[j] + ']').css('opacity', .5).find('textarea').attr('disabled', true); | |
} | |
}; | |
plugin.onOpen = function(){ | |
offline = false; | |
disableTextareas(); | |
}; | |
plugin.onClose = function(){ | |
offline = true; | |
} | |
plugin.onJoin = plugin.onLeave = disableTextareas; | |
$('.talkerapp-private-chat-head').live('click', function(){ | |
var pane = $(this).parent('.talkerapp-private-chat-pane'); | |
if (pane.hasClass('collapsed')){ | |
pane.removeClass('collapsed'); | |
pane.removeClass('new-msg'); | |
pane.find('.talkerapp-private-chat-msgbox textarea').focus(); | |
} else { | |
pane.addClass('collapsed'); | |
var ol = pane.find('ol'); | |
var divider = ol.find('li.divider'); | |
ol.append(divider.length ? divider : chatDividerHTML); | |
} | |
}); | |
$('.talkerapp-private-chat-msgbox textarea').live('keydown', function(e){ | |
e.stopPropagation(); | |
var textarea = $(this); | |
if (e.keyCode == 13){ // enter | |
e.preventDefault(); | |
var val = $.trim(textarea.val()); | |
if (val == '') return; | |
if (val == '/clear'){ | |
var pane = textarea.parents('.talkerapp-private-chat-pane'); | |
pane.find('.talkerapp-private-chat-list').html(''); | |
textarea.val('').focus(); | |
pane.data('lastTalker', ''); | |
} else { | |
var talker = textarea.parents('.talkerapp-private-chat-pane').attr('data-talker'); | |
Talker.trigger('MessageSend', { | |
type: 'message', | |
content: '/msg ' + talker + ' ' + val | |
}); | |
textarea.val('').focus(); | |
} | |
} | |
}); | |
$('#people li').live('dblclick', function(){ | |
var el = $(this); | |
var talker = $.trim(el.text()); | |
insertMsg(talker, me, null); | |
}); | |
$('.talkerapp-private-chat-head .close').live('click', function(){ | |
if (window.localStorage || confirm('Are you sure? All chat messages are NOT saved.')){ | |
var el = $(this); | |
var pane = el.parents('.talkerapp-private-chat-pane'); | |
var talker = pane.attr('data-talker'); | |
for (var i = talkers.length; i--; i){ | |
if (talkers[i] === talker) talkers.splice(i, 1); | |
} | |
pane.nextAll('.talkerapp-private-chat-pane').each(function(){ | |
$(this).animate({ | |
right: '-=' + (paneWidth+paneSpace) | |
}); | |
}); | |
pane.remove(); | |
} | |
return false; | |
}); | |
var formatTime = function(datetime){ | |
var hour = datetime.getHours(); | |
var ap = (hour >= 12) ? 'PM' : 'AM'; | |
if (hour == 0){ | |
hour = 12; | |
} else if (hour > 12){ | |
hour -= 12 | |
} | |
var min = '' + datetime.getMinutes(); | |
if (min.length == 1) min = '0' + min; | |
return hour + ':' + min + ' ' + ap; | |
}; | |
var chatwindows = {}; | |
$('.talkerapp-private-chat-head .log').live('click', function(){ | |
var el = $(this); | |
var pane = el.parents('.talkerapp-private-chat-pane'); | |
var talker = pane.attr('data-talker'); | |
var hasWinFocus = !!window.focus; | |
if (chatwindows[talker]){ | |
if (hasWinFocus) chatwindows[talker].focus(); | |
} else { | |
var win = chatwindows[talker] = window.open('javascript:""', 'talkerapp:privatechat:' + talker, 'width=400,height=400,location=0,menubar=0,toolbar=0,resizable=1,scrollbars=1'); | |
if (hasWinFocus) win.focus(); | |
var olHTML = '<li>Empty.</li>'; | |
var data = localStorage['talkerapp:privatechat:' + talker]; | |
if (data){ | |
var c = JSON.decode(data); | |
if (c.length){ | |
olHTML = ''; | |
var prevDate = ''; | |
var prevTime = ''; | |
var prevTalker = ''; | |
$.each(c, function(i, chat){ | |
var datetime = new Date(chat.datetime); | |
var date = datetime.toLocaleDateString(); | |
var nowDate = new Date().toLocaleDateString(); | |
if (date == nowDate) date = 'Today'; | |
if (date != prevDate){ | |
olHTML += _.template(chatLogItemDate, { | |
date: date | |
}); | |
prevDate = date; | |
prevTime = ''; | |
prevTalker = ''; | |
} | |
var time = formatTime(datetime); | |
(time == prevTime) ? time = ' ' : prevTime = time; | |
var talker = chat.talker; | |
if (talker == prevTalker){ | |
talker = ''; | |
} else { | |
prevTalker = talker; | |
talker += ':'; | |
} | |
olHTML += _.template(chatLogItem, { | |
time: time, | |
talker: talker, | |
msg: formatMsg(chat.msg) | |
}); | |
}); | |
} | |
} | |
var html = _.template(chatLogHTML, { | |
id: talker, | |
list: olHTML | |
}); | |
var doc = win.document; | |
doc.open(); | |
doc.write(html); | |
doc.close(); | |
win.onunload = function(){ | |
delete chatwindows[talker]; | |
} | |
} | |
return false; | |
}); | |
$('.talkerapp-private-chat-pane textarea').live('mousedown keydown click', function(){ | |
if (offline) return false; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment