public/javascripts/chat.js
/* XMPP/Jabber Noosfero's client
XMPP Core:
http://xmpp.org/rfcs/rfc3920.html
MUC support:
http://xmpp.org/extensions/xep-0045.html
Messages and presence:
http://xmpp.org/rfcs/rfc3921.html
*/
jQuery(function($) {
// extending the current namespaces in Strophe.NS
Strophe.addNamespace('MUC_USER', 'http://jabber.org/protocol/muc#user');
Strophe.addNamespace('MUC_OWNER', 'http://jabber.org/protocol/muc#owner');
Strophe.addNamespace('CHAT_STATES', 'http://jabber.org/protocol/chatstates');
Strophe.addNamespace('DATA_FORMS', 'jabber:x:data');
var Jabber = {
debug: true,
connection: null,
bosh_service: $bosh_service,
muc_domain: $muc_domain,
muc_supported: false,
presence_status: '',
conversation_prefix: 'conversation-',
conversations_order: null,
notification_sound: new Audio('/sounds/receive.wav'),
notification_counter: 0,
window_visibility: null,
window_title: document.title,
jids: {},
rooms: {},
no_more_messages: {},
avatars: {},
favico: new Favico({animation: 'popFade'}),
template: function(selector) {
return $('#chat #chat-templates '+selector).clone().html();
},
jid_to_id: function (jid) {
return Strophe.getBareJidFromJid(jid).replace(/@/g, "-").replace(/\./g, "-");
},
jid_of: function(jid_id) {
return Jabber.jids[jid_id].jid;
},
name_of: function(jid_id) {
return Jabber.jids[jid_id].name;
},
type_of: function(jid_id) {
return Jabber.jids[jid_id].type;
},
presence_of: function(jid_id) {
return Jabber.jids[jid_id].presence;
},
unread_messages_of: function(jid_id, value) {
Jabber.jids[jid_id].unread_messages = (value == undefined ? Jabber.jids[jid_id].unread_messages : value);
return Jabber.jids[jid_id].unread_messages;
},
insert_or_update_user: function (list, item, jid, name, presence, template, type, remove_on_offline) {
var jid_id = Jabber.jid_to_id(jid);
var identifier = Strophe.getNodeFromJid(jid);
var html = template
.replace('%{jid_id}', jid_id)
.replace(/%{presence_status}/g, presence)
.replace('%{avatar}', getAvatar(identifier))
.replace('%{name}', name);
$(item).parent().remove();
if(presence != 'offline' || !remove_on_offline){
$(list).append(html);
sort_conversations();
}
Jabber.jids[jid_id] = {jid: jid, name: name, type: type, presence: presence};
},
insert_or_update_group: function (jid, presence) {
var jid_id = Jabber.jid_to_id(jid);
var list = $('#buddy-list .buddies ul.'+presence);
var item = $('#' + jid_id);
presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline');
log('adding or updating contact ' + jid + ' as ' + presence);
Jabber.insert_or_update_user(list, item, jid, Jabber.name_of(jid_id), presence, Jabber.template('.buddy-item'), 'groupchat');
$("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']")
.removeClass()
.addClass('icon-menu-' + presence + '-11');
},
insert_or_update_contact: function (jid, name, presence) {
var jid_id = Jabber.jid_to_id(jid);
var item = $('#' + jid_id);
presence = presence || ($(item).length > 0 ? $(item).parent('li').attr('class') : 'offline');
var list = $('#buddy-list .buddies ul' + (presence=='offline' ? '.offline' : '.online'));
log('adding or updating contact ' + jid + ' as ' + presence);
Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.buddy-item'), 'chat');
$("#chat-window .tab a[href='#"+ Jabber.conversation_prefix + jid_id +"']")
.removeClass()
.addClass('icon-menu-' + presence + '-11');
},
insert_or_update_occupant: function (jid, name, presence, room_jid) {
log('adding or updating occupant ' + jid + ' as ' + presence);
var jid_id = Jabber.jid_to_id(jid);
var room_jid_id = Jabber.jid_to_id(room_jid);
var list = $('#' + Jabber.conversation_prefix + room_jid_id + ' .occupants ul');
var item = $(list).find('a[data-id='+ jid_id +']');
Jabber.insert_or_update_user(list, item, jid, name, presence, Jabber.template('.occupant-item'), 'chat', true);
if (Jabber.rooms[room_jid_id] === undefined)
Jabber.rooms[room_jid_id] = {};
var room = Jabber.rooms[room_jid_id];
if(presence == 'offline') {
delete Jabber.rooms[room_jid_id][name];
}
else {
Jabber.rooms[room_jid_id][name] = jid;
}
list.parents('.occupants').find('.occupants-online').text(Object.keys(Jabber.rooms[room_jid_id]).length);
},
remove_contact: function(jid) {
var jid_id = Jabber.jid_to_id(jid)
log('Removing contact ' + jid);
$('#' + jid_id).parent('li').remove();
},
render_body_message: function(body) {
body = body.replace(/\r?\n/g, '<br>');
body = $().emoticon(body);
body = linkify(body, {
callback: function(text, href) {
return href ? '<a href="' + href + '" title="' + href + '" target="_blank">' + text + '</a>' : text;
}
});
return body;
},
show_message: function (jid, name, body, who, identifier, time, offset) {
if(!offset) offset = 0;
if (body) {
body = Jabber.render_body_message(body);
var jid_id = Jabber.jid_to_id(jid);
var tab_id = '#' + Jabber.conversation_prefix + jid_id;
var history = $(tab_id).find('.history');
var offset_container = history.find('.chat-offset-container-'+offset);
if(offset_container.length == 0)
offset_container = $('<div class="chat-offset-container-'+offset+'"></div>').prependTo(history);
if (time==undefined) {
time = new Date().toISOString();
}
var message_html = Jabber.template('.message')
.replace('%{message}', body)
.replace(/%{who}/g, who)
.replace('%{time}', time)
.replace('%{name}', name)
.replace('%{avatar}', getAvatar(identifier));
offset_container.append(message_html);
$(".message span.time").timeago();
if(offset == 0) history.scrollTo({top:'100%', left:'0%'});
else history.scrollTo(offset_container.height());
if (who != "self") {
if ($(tab_id).find('.history:visible').length == 0 || !$('#chat').hasClass('opened')) {
count_unread_messages(jid_id);
}
document.alert_title = name;
}
}
},
show_status: function(presence) {
log('changing my status to ' + presence);
$('#buddy-list .user-status .simplemenu-trigger')
.removeClass('icon-menu-chat')
.removeClass('icon-menu-offline')
.removeClass('icon-menu-dnd')
.addClass('icon-menu-' + (presence || 'offline'));
$('#buddy-list #user-status img.avatar').replaceWith(getMyAvatar());
$.get('/chat/update_presence_status', { status: {chat_status: presence, last_chat_status: presence} });
},
send_availability_status: function(presence) {
log('send availability status ' + presence);
Jabber.connection.send($pres().c('show').t(presence).up());
Jabber.show_status(presence);
},
enter_room: function(jid, push) {
if(push == undefined)
push = true
var jid_id = Jabber.jid_to_id(jid);
var conversation_id = Jabber.conversation_prefix + jid_id;
var button = $('#' + conversation_id + ' .join');
button.hide();
button.siblings('.leave').show();
Jabber.connection.send(
$pres({to: jid + '/' + $own_name}).c('x', {xmlns: Strophe.NS.MUC}).c('history', {maxchars: 0})
);
Jabber.insert_or_update_group(jid, 'online');
Jabber.update_chat_title();
sort_conversations();
if(push)
$.post('/chat/join', {room_id: jid});
},
leave_room: function(jid, push) {
if(push == undefined)
push = true
var jid_id = Jabber.jid_to_id(jid);
var conversation_id = Jabber.conversation_prefix + jid_id;
var button = $('#' + conversation_id + ' .leave');
button.hide();
button.siblings('.join').show();
Jabber.connection.send($pres({from: Jabber.connection.jid, to: jid + '/' + $own_name, type: 'unavailable'}))
Jabber.insert_or_update_group(jid, 'offline');
sort_conversations();
if(push)
$.post('/chat/leave', {room_id: jid});
},
update_chat_title: function () {
var friends_online = $('#buddy-list #friends .buddy-list.online li').length;
$('#friends-online').text(friends_online);
var friends_offline = $('#buddy-list #friends .buddy-list.offline li').length;
$('#friends-offline').text(friends_offline);
var groups_online = $('#buddy-list #rooms .buddy-list li').length;
$('#groups-online').text(groups_online);
},
on_connect: function (status) {
switch (status) {
case Strophe.Status.CONNECTING:
log('connecting...');
break;
case Strophe.Status.CONNFAIL:
log('failed to connect');
setTimeout(function(){Jabber.connect()}, 10000);
break;
case Strophe.Status.DISCONNECTING:
log('disconnecting...');
$('#buddy-list .toolbar').addClass('small-loading-dark');
break;
case Strophe.Status.DISCONNECTED:
log('disconnected');
$('#buddy-list ul.buddy-list, .occupants ul.occupant-list').html('');
Jabber.update_chat_title();
$('#buddy-list .toolbar').removeClass('small-loading-dark');
$('textarea').prop('disabled', 'disabled');
if(Jabber.presence_status != 'offline')
Jabber.connect();
break;
case Strophe.Status.CONNECTED:
log('connected');
case Strophe.Status.ATTACHED:
log('XMPP/BOSH session attached');
$('#buddy-list .toolbar').removeClass('small-loading-dark');
$('textarea').prop('disabled', '');
break;
}
},
on_roster: function (iq) {
log('receiving roster');
var profiles = [];
var contacts_to_insert = {};
var groups_to_insert = [];
//FIXME User ejabberd roster when the username length limit bug is solved.
// $(iq).find('item').each(function () {
// var jid = $(this).attr('jid');
// profiles.push(getIdentifier(jid));
// var name = $(this).attr('name') || jid;
// var jid_id = Jabber.jid_to_id(jid);
// contacts_to_insert[jid] = name;
// });
$.ajax({
url: '/chat/rosters',
dataType: 'json',
success: function(data){
$(data.friends).each(function(index, friend){
var jid = friend.jid;
profiles.push(getIdentifier(jid));
var name = friend.name;
var jid_id = Jabber.jid_to_id(jid);
contacts_to_insert[jid] = name;
});
$(data.rooms).each(function(index, room){
profiles.push(getIdentifier(room.jid));
var jid_id = Jabber.jid_to_id(room.jid);
Jabber.jids[jid_id] = {jid: room.jid, name: room.name, type: 'groupchat'};
//FIXME This must check on session if the user is inside the room...
groups_to_insert.push(room.jid);
});
$.post('/chat/avatars', {profiles: profiles}, function(data) {
for(identifier in data)
Jabber.avatars[identifier] = data[identifier];
// Insert contacts
for(contact_jid in contacts_to_insert)
Jabber.insert_or_update_contact(contact_jid, contacts_to_insert[contact_jid]);
// Insert groups
for (var i = 0; i < groups_to_insert.length; i++)
Jabber.insert_or_update_group(groups_to_insert[i], 'offline');
$.getJSON('/chat/recent_conversations', {}, function(data) {
Jabber.conversations_order = data;
sort_conversations();
});
// set up presence handler and send initial presence
Jabber.connection.addHandler(Jabber.on_presence, null, "presence");
Jabber.send_availability_status(Jabber.presence_status);
load_defaults();
updateAvailabilities();
}, 'json');
},
error: function(data, textStatus, jqXHR){
console.log(data);
},
});
},
// NOTE: cause Noosfero store's rosters in database based on friendship relation between people
// these event never occurs cause jabber service (ejabberd) didn't know when a roster was changed
on_roster_changed: function (iq) {
log('roster changed');
$(iq).find('item').each(function () {
var sub = $(this).attr('subscription');
var jid = $(this).attr('jid');
var name = $(this).attr('name') || jid;
if (sub == 'remove') {
// contact is being removed
Jabber.remove_contact(jid);
} else {
// contact is being added or modified
Jabber.insert_or_update_contact(jid, name);
}
});
return true;
},
parse: function (stanza) {
var result = {};
if (Strophe.isTagEqual(stanza, 'presence')) {
result.from = $(stanza).attr('from');
result.type = $(stanza).attr('type');
if (result.type == 'unavailable') {
result.show = 'offline';
} else {
var show = $(stanza).find("show").text();
if (show === "" || show == "chat") {
result.show = 'chat';
}
else if (show == "dnd" || show == "xa") {
result.show = 'dnd';
}
else {
result.show = 'away';
}
}
if ($(stanza).find('x[xmlns="'+ Strophe.NS.MUC_USER +'"]').length > 0) {
result.is_from_room = true;
result.from_user = $(stanza).find('x item').attr('jid');
if ($(stanza).find('x item').attr('affiliation') == 'owner') {
result.awaiting_configuration = ($(stanza).find('x status').attr('code') == '201');
}
}
}
else if (Strophe.isTagEqual(stanza, 'message')) {
result.from = $(stanza).attr('from');
result.body = $(stanza).find('body').text();
if ($(stanza).find('error').length > 0) {
result.error = $(stanza).find('error text').text();
if (!result.error && $(stanza).find('error').find('service-unavailable').length > 0) {
result.error = $user_unavailable_error;
}
}
}
return result;
},
on_presence: function (presence) {
presence = Jabber.parse(presence);
if (presence.type != 'error') {
if (presence.is_from_room) {
log('receiving room presence from ' + presence.from + ' as ' + presence.show);
var name = Strophe.getResourceFromJid(presence.from);
if (presence.from_user) {
Jabber.insert_or_update_occupant(presence.from_user, name, presence.show, presence.from);
}
else {
log('ooops! user jid not found in presence stanza');
}
if (presence.awaiting_configuration) {
log('sending instant room configuration to ' + Strophe.getBareJidFromJid(presence.from));
Jabber.connection.sendIQ(
$iq({type: 'set', to: Strophe.getBareJidFromJid(presence.from)})
.c('query', {xmlns: Strophe.NS.MUC_OWNER})
.c('x', {xmlns: Strophe.NS.DATA_FORMS, type: 'submit'})
);
}
}
else {
log('receiving contact presence from ' + presence.from + ' as ' + presence.show);
var jid = Strophe.getBareJidFromJid(presence.from);
setFriendStatus(jid, presence.show);
}
}
return true;
},
on_private_message: function (message) {
message = Jabber.parse(message);
log('receiving message from ' + message.from);
var jid = Strophe.getBareJidFromJid(message.from);
var jid_id = Jabber.jid_to_id(jid);
var name = Jabber.name_of(jid_id);
create_conversation_tab(name, jid_id);
Jabber.show_message(jid, name, escape_html(message.body), 'other', Strophe.getNodeFromJid(jid));
renew_conversation_order(jid);
notifyMessage(message);
return true;
},
on_public_message: function (message) {
message = Jabber.parse(message);
log('receiving message from ' + message.from);
var name = Strophe.getResourceFromJid(message.from);
// is a message from the room itself
if (! name) {
// FIXME Ignoring message from room for now.
// Jabber.show_notice(Jabber.jid_to_id(message.from), message.body);
}
// is a message from another user, not mine
else if ($own_name != name) {
var jid = Jabber.rooms[Jabber.jid_to_id(message.from)][name];
Jabber.show_message(message.from, name, escape_html(message.body), name, Strophe.getNodeFromJid(jid));
renew_conversation_order(jid);
notifyMessage(message);
}
return true;
},
on_message_error: function (message) {
},
on_muc_support: function(iq) {
if ($(iq).find('identity[category=conference]').length > 0 && $(iq).find('feature[var="'+ Strophe.NS.MUC +'"]').length > 0) {
var name = $(iq).find('identity[category=conference]').attr('name');
log('muc support found with identity '+ name);
Jabber.muc_supported = true;
}
else {
log('muc support not found');
}
},
attach_connection: function(data) {
// create the connection and attach it
Jabber.connection = new Strophe.Connection(Jabber.bosh_service);
Jabber.connection.attach(data.jid, data.sid, data.rid, Jabber.on_connect);
// handle get roster list (buddy list)
Jabber.connection.sendIQ($iq({type: 'get'}).c('query', {xmlns: Strophe.NS.ROSTER}), Jabber.on_roster);
// handle presence updates in roster list
Jabber.connection.addHandler(Jabber.on_roster_changed, 'jabber:iq:roster', 'iq', 'set');
// Handle messages
Jabber.connection.addHandler(Jabber.on_private_message, null, "message", "chat");
// Handle conference messages
Jabber.connection.addHandler(Jabber.on_public_message, null, "message", "groupchat");
// Handle message errors
Jabber.connection.addHandler(Jabber.on_message_error, null, "message", "error");
// discovering MUC support
Jabber.connection.sendIQ(
$iq({type: 'get', from: Jabber.connection.jid, to: Jabber.muc_domain})
.c('query', {xmlns: Strophe.NS.DISCO_INFO}),
Jabber.on_muc_support
);
// uncomment for extra debugging
//Strophe.log = function (lvl, msg) { log(msg); };
},
connect: function() {
if (("Notification" in window) && Notification.permission !== "granted" && Notification.permission !== "denied") {
Notification.requestPermission(function (permission) {
if (!('permission' in Notification)) {
Notification.permission = permission;
}
});
}
if (Jabber.connection && Jabber.connection.connected) {
Jabber.send_availability_status(Jabber.presence_status);
}
else {
log('starting XMPP/BOSH session...');
$('#buddy-list .toolbar').removeClass('small-loading-dark').addClass('small-loading-dark');
$('.dialog-error').hide();
$.ajax({
url: '/chat/start_session',
dataType: 'json',
success: function(data) {
Jabber.attach_connection(data)
},
error: function(error) {
$('#buddy-list .toolbar').removeClass('small-loading-dark');
$('#buddy-list .dialog-error')
.html(error.responseText)
.show('highlight')
.unbind('click')
.click(function() { $(this).hide('highlight'); });
}
});
}
},
deliver_message: function(jid, body) {
var jid_id = Jabber.jid_to_id(jid);
var type = Jabber.type_of(jid_id);
var presence = Jabber.presence_of(jid_id);
var message = $msg({to: jid, from: Jabber.connection.jid, "type": type})
.c('body').t(body).up()
.c('active', {xmlns: Strophe.NS.CHAT_STATES});
Jabber.connection.send(message);
Jabber.show_message(jid, $own_name, escape_html(body), 'self', Strophe.getNodeFromJid(Jabber.connection.jid));
save_message(jid, body);
renew_conversation_order(jid);
move_conversation_to_the_top(jid);
if (presence == 'offline')
Jabber.show_notice(jid_id, $user_unavailable_error);
},
is_a_room: function(jid_id) {
return Jabber.type_of(jid_id) == 'groupchat';
},
show_notice: function(jid_id, msg) {
var tab_id = '#' + Jabber.conversation_prefix + jid_id;
var history = $(tab_id).find('.history');
var notice = $(tab_id).find('.history .notice');
if (notice.length > 0)
notice.html(msg)
else
$(tab_id).find('.history').append("<span class='notice'>" + msg + "</span>");
history.scrollTo({top:'100%', left:'0%'});
},
remove_notice: function(jid_id) {
var tab_id = '#' + Jabber.conversation_prefix + jid_id;
var notice = $(tab_id).find('.history .notice').remove();
},
};
$('#chat-connect').live('click', function() {
Jabber.presence_status = 'chat';
Jabber.connect();
$('#chat .simplemenu-submenu').hide();
return false;
});
$('#chat-disconnect').click(function() {
disconnect();
$('#chat .simplemenu-submenu').hide();
return false;
});
$('#chat-busy').click(function() {
Jabber.presence_status = 'dnd';
Jabber.connect();
$('#chat .simplemenu-submenu').hide();
return false;
});
$('#chat-retry').live('click', function() {
Jabber.presence_status = Jabber.presence_status || 'chat';
Jabber.connect();
return false;
});
$('.conversation textarea').live('keydown', function(e) {
if (e.keyCode == 13) {
var jid = $(this).attr('data-to');
var body = $(this).val();
body = $('<div>'+ body +'</div>').find('script,noscript,style').remove().end().html();
Jabber.deliver_message(jid, body);
$(this).val('');
return false;
}
});
function save_message(jid, body) {
$.post('/chat/save_message', {
to: getIdentifier(jid),
body: body
}, function(data){
if(data.status > 0){
console.log(data.message);
if(data.backtrace) console.log(data.backtrace);
}
}).fail(function(){
console.log('500 - Internal server error.')
});
}
// open new conversation or change to already opened tab
$('#buddy-list .buddies li a').live('click', function() {
var jid = Jabber.jid_of($(this).attr('id'));
open_conversation(jid);
return false;
});
// put name into text area when click in one occupant
$('.occupants .occupant-list li a').live('click', function() {
var jid_id = $(this).attr('data-id');
var name = Jabber.name_of(jid_id);
var val = $('.conversation textarea:visible').val();
$('.conversation textarea:visible').focus().val(val + name + ', ');
return false;
});
$('#chat .conversation .history').live('click', function() {
$('.conversation textarea:visible').focus();
});
function toggle_chat_window() {
if(jQuery('#conversations .conversation').length == 0) jQuery('.buddies a').first().click();
jQuery('#chat').toggleClass('opened');
jQuery('#chat-label').toggleClass('opened');
if($('#chat').hasClass('opened')) {
var jid_id = $('#chat-window .conversation:visible').attr('id').replace(/^conversation-/, '');
count_unread_messages(jid_id, true);
}
}
function load_conversation(jid) {
var jid_id = Jabber.jid_to_id(jid);
var name = Jabber.name_of(jid_id);
if (jid) {
if (Strophe.getDomainFromJid(jid) == Jabber.muc_domain) {
if (Jabber.muc_supported) {
log('opening groupchat with ' + jid);
Jabber.jids[jid_id] = {jid: jid, name: name, type: 'groupchat'};
var conversation = create_conversation_tab(name, jid_id);
Jabber.enter_room(jid);
recent_messages(jid, 0, true);
return conversation;
}
}
else {
log('opening chat with ' + jid);
Jabber.jids[jid_id] = {jid: jid, name: name, type: 'chat'};
var conversation = create_conversation_tab(name, jid_id);
recent_messages(jid, 0, true);
return conversation;
}
}
}
function update_notification_counter() {
Jabber.notification_counter += 1;
Jabber.favico.badge(Jabber.notification_counter);
document.title = '(' + Jabber.notification_counter + ') ' + Jabber.window_title;
}
function open_conversation(jid) {
var conversation = load_conversation(jid);
var jid_id = Jabber.jid_to_id(jid);
$('.conversation').hide();
conversation.show();
conversation.find('.input').focus();
$('#chat').addClass('opened');
$('#chat-label').addClass('opened');
$.post('/chat/tab', {tab_id: jid_id});
}
function create_conversation_tab(title, jid_id) {
var conversation_id = Jabber.conversation_prefix + jid_id;
var conversation = $('#' + conversation_id);
if (conversation.length > 0) {
return conversation;
}
var jid = Jabber.jid_of(jid_id);
var identifier = getIdentifier(jid);
var panel = $('#chat-templates .conversation').clone().appendTo($conversations).attr('id', conversation_id);
panel.find('.chat-target .avatar').replaceWith(getAvatar(identifier));
panel.find('.chat-target .other-name').html(title);
$('#chat .history').perfectScrollbar();
panel.find('.history').scroll(function(){
if($(this).scrollTop() == 0){
var offset = panel.find('.message p').size();
recent_messages(jid, offset);
}
});
var textarea = panel.find('textarea');
textarea.attr('name', panel.id);
if (Jabber.is_a_room(jid_id)) {
panel.append(Jabber.template('.occupant-list-template'));
panel.find('.history').addClass('room');
var room_actions = $('#chat-templates .room-action').clone();
room_actions.data('jid', jid);
panel.find('.history').after(room_actions);
$('#chat .occupants .occupant-list').perfectScrollbar();
}
textarea.attr('data-to', jid);
return panel;
}
function ensure_scroll(jid, offset) {
var jid_id = Jabber.jid_to_id(jid);
var history = jQuery('#conversation-'+jid_id+' .history');
// Load more messages if was not enough to show the scroll
if(history.prop('scrollHeight') - history.prop('clientHeight') <= 0){
var offset = history.find('.message p').size();
recent_messages(jid, offset);
}
}
function recent_messages(jid, offset, clear_unread) {
if (Jabber.no_more_messages[jid]) return;
if(!offset) offset = 0;
start_fetching('.history');
$.getJSON('/chat/recent_messages', {identifier: getIdentifier(jid), offset: offset}, function(data) {
// Register if no more messages returned and stop trying to load
// more messages in the future.
if(data.length == 0)
Jabber.no_more_messages[jid] = true;
$.each(data, function(i, message) {
var body = message['body'];
var from = message['from'];
var to = message['to'];
var date = message['created_at'];
var who = from['id']==getCurrentIdentifier() ? 'self' : from['id']
Jabber.show_message(jid, from['name'], body, who, from['id'], date, offset);
});
stop_fetching('.history');
ensure_scroll(jid, offset);
if(clear_unread){
var jid_id = Jabber.jid_to_id(jid);
count_unread_messages(jid_id, true);
}
});
}
function move_conversation_to_the_top(jid) {
id = Jabber.jid_to_id(jid);
var link = $('#'+id);
var li = link.closest('li');
var ul = link.closest('ul');
ul.prepend(li);
}
function renew_conversation_order(jid){
var i = Jabber.conversations_order.indexOf(jid);
// Remove element from the list
if(i >= 0) {
var elem = Jabber.conversations_order[i];
var a = Jabber.conversations_order.slice(0,i);
var b = Jabber.conversations_order.slice(i+1, Jabber.conversations_order.length);
Jabber.conversations_order = a.concat(b);
} else
var elem = jid;
Jabber.conversations_order = Jabber.conversations_order.concat(elem);
}
function sort_conversations() {
if(Jabber.conversations_order){
for (var i = 0; i < Jabber.conversations_order.length; i++)
move_conversation_to_the_top(Jabber.conversations_order[i]);
}
}
function load_defaults() {
$.getJSON('/chat/my_session', {}, function(data) {
$.each(data.rooms, function(i, room_jid) {
load_conversation(room_jid);
})
$('#'+data.tab_id).click();
if(data.status == 'opened')
toggle_chat_window();
})
}
function count_unread_messages(jid_id, hide) {
var unread = $('.buddies #'+jid_id+ ' .unread-messages');
if (hide) {
unread.hide();
Jabber.unread_messages_of(jid_id, 0);
unread.text('');
}
else {
var unread_messages = Jabber.unread_messages_of(jid_id) || 0;
Jabber.unread_messages_of(jid_id, ++unread_messages);
unread.text(unread_messages);
}
update_total_unread_messages();
}
function update_total_unread_messages() {
var total_unread = $('#unread-messages');
var sum = 0;
$('#chat .unread-messages').each(function() {
sum += Number($(this).text());
});
if(sum>0) {
total_unread.text(sum);
} else {
total_unread.text('');
}
}
var $conversations = $('#chat-window #conversations');
function log(msg) {
if(Jabber.debug && window.console && window.console.log) {
var time = new Date();
window.console.log('['+ time.toTimeString() +'] ' + msg);
}
}
function escape_html(body) {
return body
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
}
function getCurrentIdentifier() {
return getIdentifier(Jabber.connection.jid);
}
function getIdentifier(jid) {
return Strophe.getNodeFromJid(jid);
}
function getMyAvatar() {
return getAvatar(getCurrentIdentifier());
}
function getAvatar(identifier) {
if(Jabber.avatars[identifier])
var src = Jabber.avatars[identifier];
else
var src = "/chat/avatar/"+identifier;
return '<img class="avatar" src="' + src + '">';
}
function disconnect() {
log('disconnect');
if (Jabber.connection && Jabber.connection.connected) {
Jabber.connection.disconnect();
}
Jabber.presence_status = 'offline';
Jabber.show_status('offline');
}
function notifyMessage(message) {
if(!("Notification" in window))
return null;
var jid = Strophe.getBareJidFromJid(message.from);
var jid_id = Jabber.jid_to_id(jid);
var name = Jabber.name_of(jid_id);
var identifier = Strophe.getNodeFromJid(jid);
var avatar = "/chat/avatar/"+identifier
if(window.isHidden() || !Jabber.window_visibility) {
update_notification_counter();
}
if(!$('#chat').hasClass('opened') || window.isHidden() || !Jabber.window_visibility) {
var options = {body: message.body, icon: avatar, tag: jid_id};
$(notifyMe(name, options)).on('click', function(){
open_conversation(jid);
});
Jabber.notification_sound.play();
}
}
function setFriendStatus(jid, status) {
if (jid != Jabber.connection.jid) {
var jid_id = Jabber.jid_to_id(jid);
var name = Jabber.name_of(jid_id);
if(status == 'chat')
Jabber.remove_notice(jid_id);
Jabber.insert_or_update_contact(jid, name, status);
Jabber.update_chat_title();
}
else {
// why server sends presence from myself to me?
log('ignoring presence from myself');
if(status=='offline') {
Jabber.send_availability_status(Jabber.presence_status);
}
}
}
function resetNotificationCounter() {
Jabber.favico.reset();
Jabber.notification_counter = 0;
document.title = Jabber.window_title;
}
$('.title-bar a').click(function() {
$(this).parents('.status-group').find('.buddies').toggle('fast');
return false;
});
$('#chat').on('click', '.occupants a', function() {
$(this).siblings('.occupant-list').toggle('fast');
$(this).toggleClass('up');
return false;
});
//restore connection if user was connected
if($presence=='' || $presence == 'chat') {
$('#chat-connect').trigger('click');
} else if($presence == 'dnd') {
$('#chat-busy').trigger('click');
}
$('#chat #buddy-list .buddies').perfectScrollbar();
// custom css expression for a case-insensitive contains()
jQuery.expr[':'].Contains = function(a,i,m){
return (a.textContent || a.innerText || "").toUpperCase().indexOf(m[3].toUpperCase())>=0;
};
$('#chat .search').change( function () {
var filter = $(this).val();
var list = $('#buddy-list .buddies a');
if(filter) {
// this finds all links in a list that contain the input,
// and hide the ones not containing the input while showing the ones that do
$(list).find("span:not(:Contains(" + filter + "))").parent().hide();
$(list).find("span:Contains(" + filter + ")").parent().show();
} else {
$(list).show();
}
return false;
}).keyup( function () {
// fire the above change event after every letter
$(this).change();
});
$('#chat .buddies a').live('click', function(){
$('#chat .search').val('').change();
return false;
});
$('#chat-label').click(function(){
toggle_chat_window();
resetNotificationCounter();
$.post('/chat/toggle');
});
$('.room-action.join').live('click', function(){
var jid = $(this).data('jid');
Jabber.enter_room(jid);
});
$('.room-action.leave').live('click', function(){
var jid = $(this).data('jid');
Jabber.leave_room(jid);
return false;
});
$('.open-conversation').live('click', function(){
open_conversation($(this).data('jid'));
return false;
});
window.onfocus = function() {
Jabber.window_visibility = true;
resetNotificationCounter();
};
window.onblur = function() { Jabber.window_visibility = false };
//FIXME Workaround to solve availability problems
function updateAvailabilities() {
$.ajax({
url: '/chat/availabilities',
dataType: 'json',
success: function(data){
$(data).each(function(index, friend){
var jid_id = Jabber.jid_to_id(friend.jid);
if (Jabber.jids[jid_id].presence != friend.status)
setFriendStatus(friend.jid, friend.status)
});
},
complete: function(data){ setTimeout(updateAvailabilities, 10000) },
error: function(data, textStatus, jqXHR){
console.log(data);
},
});
}
});