112:f4a4878c99a3
Anton Shestakov <av6@dwimlabs.net>, Thu, 07 Apr 2016 22:56:16 +0800
index: check if roster has the item before removing it Sometimes we get events from contacts that are not in user's roster, trying to remove such contacts used to fail before this patch.

next change 120:b71b4dae9feb
previous change 111:b2e3a00a9691

js/index.js

Permissions: -rw-r--r--

Other formats: Feeds:
// Generated by CoffeeScript 1.10.0
(function() {
var $form, ConnectionData, connectfn, getChat, getContact, getContactProfile, getText, getVersion, onChatMessage, onConnected, onDisconnected, onGetLast, onGetTime, onGetVersion, onPing, onPresence, onProfileUpdate, onWebRTC, sendMessage,
extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
hasProp = {}.hasOwnProperty,
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
Strophe.addNamespace('LAST', 'jabber:iq:last');
Strophe.addNamespace('TIME', 'urn:xmpp:time');
Strophe.addNamespace('VCARD_UPDATE', 'vcard-temp:x:update');
window.Tram.ClientState = (function(superClass) {
extend(ClientState, superClass);
function ClientState() {
return ClientState.__super__.constructor.apply(this, arguments);
}
ClientState.prototype.defaults = {
show: 'offline'
};
return ClientState;
})(Backbone.Model);
window.contacts = new Tram.Contacts();
window.contactsApp = new Tram.ContactsApp({
el: $('[data-app="contacts"]'),
collection: contacts
});
window.calls = new Tram.Calls();
window.callsApp = new Tram.CallsApp({
el: $('[data-app="calls"]'),
collection: calls
});
window.clientState = new Tram.ClientState();
window.faviconApp = new Tram.FaviconApp({
model: clientState
});
window.sidebarApp = new Tram.SidebarApp({
el: $('[data-app="sidebar"]'),
model: clientState
});
window.chats = {};
contacts.on('change:show', function(model) {
if (model.get('type') === 'self') {
return clientState.set('show', model.get('show'));
}
});
clientState.on('action/show', function(show, status) {
return X.conn.send($pres().c('priority').t('1').up().c('show').t(show).up().c('status').t(status).tree());
});
clientState.on('action/disconnect', function() {
return X.disconnect('Logged out');
});
clientState.on('change:contact', function() {
var $logs, contact;
contact = clientState.get('contact');
$logs = $('[data-app="logs"]');
$logs.children().detach();
$logs.append(contact.chat.render().el);
return $('[data-form="send"]').toggleClass('uk-hidden', !clientState.has('contact'));
});
onConnected = function() {
X.conn.addHandler(onPresence, null, 'presence');
X.conn.addHandler(onProfileUpdate, Strophe.NS.VCARD_UPDATE, 'presence');
X.conn.addHandler(onChatMessage, null, 'message', 'chat');
X.conn.addHandler(onWebRTC, Tram.NS.WEBRTC, 'message', 'chat');
X.conn.addHandler(onGetLast, Strophe.NS.LAST, 'iq', 'get');
X.conn.addHandler(onGetTime, Strophe.NS.TIME, 'iq', 'get');
X.conn.addHandler(onGetVersion, Strophe.NS.VERSION, 'iq', 'get');
X.conn.ping.addPingHandler(onPing);
X.conn.disco.addIdentity('client', 'web', Tram.info.client);
X.conn.disco.addFeature(Strophe.NS.DISCO_INFO);
X.conn.disco.addFeature(Strophe.NS.LAST);
X.conn.disco.addFeature(Strophe.NS.PING);
X.conn.disco.addFeature(Strophe.NS.TIME);
X.conn.disco.addFeature(Strophe.NS.VERSION);
X.conn.disco.addFeature(Tram.NS.WEBRTC);
X.conn.send($pres().c('priority').t('1').up().c('status').t('Online').tree());
getVersion();
return X.conn.roster.get();
};
onDisconnected = function() {
return location.reload();
};
getText = function(el) {
var i, len, node, ref, str;
if (!el) {
return null;
}
str = '';
if (el.childNodes.length === 0 && el.nodeType === Strophe.ElementType.TEXT) {
str += el.nodeValue;
}
ref = el.childNodes;
for (i = 0, len = ref.length; i < len; i++) {
node = ref[i];
if (node.nodeType === Strophe.ElementType.TEXT) {
str += node.nodeValue;
}
}
return str;
};
getVersion = function() {
var failcb, iq, okcb;
iq = $iq({
type: 'get',
id: X.conn.getUniqueId('version'),
to: Tram.config.domain
}).c('query', {
xmlns: Strophe.NS.VERSION
});
okcb = function(stanza) {
var name, os, version;
name = getText(stanza.getElementsByTagName('name')[0]);
version = getText(stanza.getElementsByTagName('version')[0]);
os = getText(stanza.getElementsByTagName('os')[0]);
return clientState.set({
'server/name': name,
'server/version': version,
'server/os': os
});
};
failcb = function(stanza) {
return console.error("couldn't get version", stanza != null ? stanza.innerHTML : void 0);
};
return X.conn.sendIQ(iq.tree(), okcb, failcb, 5000);
};
onGetLast = function(stanza) {
var from, id, iq;
id = stanza.getAttribute('id');
from = stanza.getAttribute('from');
iq = $iq({
to: from,
type: 'result',
id: id
}).c('query', {
xmlns: Strophe.NS.VERSION,
seconds: '0'
});
X.conn.send(iq.tree());
return true;
};
onGetTime = function(stanza) {
var from, id, iq, now;
now = moment();
id = stanza.getAttribute('id');
from = stanza.getAttribute('from');
iq = $iq({
to: from,
type: 'result',
id: id
}).c('time', {
xmlns: Strophe.NS.TIME
}).c('tzo').t(now.format('Z')).up().c('utc').t(now.toISOString());
X.conn.send(iq.tree());
return true;
};
onGetVersion = function(stanza) {
var from, id, iq;
id = stanza.getAttribute('id');
from = stanza.getAttribute('from');
iq = $iq({
to: from,
type: 'result',
id: id
}).c('query', {
xmlns: Strophe.NS.VERSION
}).c('name').t(Tram.info.client).up().c('version').t(Tram.info.version);
X.conn.send(iq.tree());
return true;
};
onPing = function(stanza) {
X.conn.ping.pong(stanza);
return true;
};
getChat = function(jid) {
return chats[jid] != null ? chats[jid] : chats[jid] = new Tram.LogApp({
collection: new Tram.Messages()
});
};
getContact = function(from) {
var bjid, contact, self;
contact = contacts.get(from);
if (contact != null) {
return contact;
}
self = from === X.conn.jid;
if (self) {
contacts.each(function(model) {
return model.set('type', 'contact');
});
}
bjid = Strophe.getBareJidFromJid(from);
contact = contacts.add({
jid: from,
bjid: bjid,
type: self ? 'self' : 'contact'
});
contact.chat = getChat(bjid);
contact.on('action/chat', function() {
return clientState.set('contact', contact);
});
contact.on('action/authorize', function() {
return X.conn.roster.authorize(contact.get('bjid'));
});
contact.on('action/unauthorize', function() {
X.conn.roster.unauthorize(contact.get('bjid'));
return contacts.remove(contact);
});
contact.on('action/remove', function() {
if (X.conn.roster.findItem(contact.get('bjid'))) {
return X.conn.roster.remove(contact.get('bjid'), function() {
return contacts.remove(contact);
});
} else {
return contacts.remove(contact);
}
});
contact.w = new Tram.WebRTCInterface(contact);
contact.on('action/call', function(media) {
contact.set('callstate', 'outgoing');
return contact.w.init(true, {
audio: indexOf.call(media, 'a') >= 0,
video: indexOf.call(media, 'v') >= 0
});
});
contact.on('action/accept', function(media) {
return contact.w.init(false, {
audio: indexOf.call(media, 'a') >= 0,
video: indexOf.call(media, 'v') >= 0
});
});
contact.on('action/decline action/hangup', function() {
contact.w.sendIntent('terminate');
contact.w.disconnect();
return contact.unset('callstate');
});
getContactProfile(contact);
return contact;
};
getContactProfile = function(contact) {
var bjid, failcb, okcb;
okcb = function(stanza) {
var data, mime, vcard;
vcard = stanza.getElementsByTagName('vCard')[0];
if (!vcard) {
console.warn("no vcard in response", stanza);
return;
}
contact.set({
nickname: getText(vcard.querySelector('NICKNAME')),
fullname: getText(vcard.querySelector('FN')),
firstname: getText(vcard.querySelector('N > GIVEN')),
lastname: getText(vcard.querySelector('N > FAMILY'))
});
mime = getText(vcard.querySelector('PHOTO > TYPE'));
data = getText(vcard.querySelector('PHOTO > BINVAL'));
if (mime && data) {
return contact.set('avatar', {
mime: mime,
data: data
});
} else {
return contact.unset('avatar');
}
};
failcb = function(stanza) {
return console.warn("couldn't get vcard", stanza);
};
bjid = contact.get('bjid');
if (bjid === Strophe.getBareJidFromJid(X.conn.jid)) {
bjid = null;
}
return X.conn.vcard.get(okcb, bjid, failcb);
};
onPresence = function(stanza) {
var contact, delay, from, priority, ref, show, stamp, status, type;
type = (ref = stanza.getAttribute('type')) != null ? ref : 'available';
from = stanza.getAttribute('from');
switch (type) {
case 'unsubscribe':
console.warn("not handling <presence type=\"" + type + "\">", stanza);
return true;
case 'subscribed':
X.conn.roster.subscribe(from);
return true;
case 'unsubscribed':
X.conn.roster.unsubscribe(from);
return true;
case 'error':
console.error('got <presence type="error">', stanza);
return true;
}
contact = getContact(from);
show = getText(stanza.getElementsByTagName('show')[0]);
status = getText(stanza.getElementsByTagName('status')[0]);
priority = getText(stanza.getElementsByTagName('priority')[0]);
switch (type) {
case 'available':
if (show == null) {
show = 'online';
}
if (priority == null) {
priority = '0';
}
break;
case 'unavailable':
show = 'offline';
priority = '0';
}
contact.set({
presence: type,
show: show,
status: status,
priority: priority
});
if (type === 'unavailable') {
contact.w.disconnect();
}
if ((type === 'available' || type === 'unavailable') && contact.get('type') !== 'self') {
delay = stanza.getElementsByTagName('delay')[0];
stamp = delay != null ? new Date(delay.getAttribute('stamp')) : new Date();
contact.chat.collection.add({
id: stanza.getAttribute('id'),
type: 'presence',
cls: 'presence',
from: from,
stamp: stamp,
contact: contact,
presence: type,
show: show,
status: status,
priority: priority,
text: status != null ? status : "is now " + show
});
}
return true;
};
onProfileUpdate = function(stanza) {
var contact, from;
from = stanza.getAttribute('from');
contact = getContact(from);
getContactProfile(contact);
return true;
};
onChatMessage = function(stanza) {
var contact, delay, from, stamp, text, thread, type;
from = stanza.getAttribute('from');
type = stanza.getAttribute('type');
text = getText(stanza.getElementsByTagName('body')[0]);
thread = getText(stanza.getElementsByTagName('thread')[0]);
delay = stanza.getElementsByTagName('delay')[0];
stamp = delay != null ? new Date(delay.getAttribute('stamp')) : new Date();
contact = getContact(from);
if (text) {
contact.chat.collection.add({
id: stanza.getAttribute('id'),
type: type,
cls: type,
from: from,
stamp: stamp,
contact: contact,
thread: thread,
text: text
});
}
return true;
};
onWebRTC = function(stanza) {
var contact, delay, from, intent, payload, stamp;
from = stanza.getAttribute('from');
contact = getContact(from);
intent = stanza.getElementsByTagName('intent')[0];
payload = stanza.getElementsByTagName('payload')[0];
delay = stanza.getElementsByTagName('delay')[0];
stamp = delay != null ? new Date(delay.getAttribute('stamp')) : new Date();
if (intent != null) {
switch (getText(intent)) {
case 'initiate':
contact.set('callstate', 'incoming');
contact.chat.collection.add({
id: stanza.getAttribute('id'),
type: 'call',
cls: 'call',
from: from,
stamp: stamp,
contact: contact,
text: 'incoming call'
});
break;
case 'terminate':
contact.w.disconnect();
contact.unset('callstate');
}
}
if (payload != null) {
contact.w.onPayload(stanza);
}
return true;
};
window.X = new Tram.XMPPInterface();
X.on('connecting', function() {
return connData.unset('auth-errors');
});
X.on('authfail', function() {
return connData.set('auth-errors', ['Invalid username or password.']);
});
X.on('disconnected', function() {
$('[data-step="login"]').removeClass('uk-hidden');
$('[data-step="main"]').addClass('uk-hidden');
return onDisconnected();
});
X.on('connected attached', function() {
$('[data-step="login"]').addClass('uk-hidden');
$('[data-step="main"]').removeClass('uk-hidden');
return onConnected();
});
$(window).on('beforeunload unload', function() {
return X.disconnect('Window closed');
});
ConnectionData = (function(superClass) {
extend(ConnectionData, superClass);
function ConnectionData() {
return ConnectionData.__super__.constructor.apply(this, arguments);
}
ConnectionData.prototype.defaults = {
username: '',
password: ''
};
ConnectionData.prototype.validate = function(attrs, options) {
var ref, ref1;
this.unset('username-errors');
this.unset('password-errors');
this.unset('auth-errors');
if (((ref = attrs.username) != null ? ref : '').trim() === '') {
this.set('username-errors', ['This field is required.']);
}
if (((ref1 = attrs.password) != null ? ref1 : '') === '') {
this.set('password-errors', ['This field is required.']);
}
return this.has('username-errors') || this.has('password-errors');
};
return ConnectionData;
})(Backbone.Model);
window.connData = new ConnectionData();
connectfn = function() {
if (connData.isValid()) {
return X.connect(connData.get('username').trim(), connData.get('password'));
}
};
$form = $('[data-form="connect"]');
window.connRivet = rivets.bind($form.get(0), {
data: connData,
connect: connectfn
});
$form.find('input').on('keydown', function(e) {
var $next, index;
if ((!this.required || this.value !== '') && e.keyCode === 13) {
e.preventDefault();
index = $form.find('input').index(this);
$next = $form.find('input').eq(index + 1);
if ($next.length !== 0) {
return $next.focus();
} else {
return $form.find('button').trigger('click');
}
}
});
sendMessage = function() {
var contact, text;
text = $('#msg').val();
if (text !== '' && clientState.has('contact')) {
contact = clientState.get('contact');
X.conn.send($msg({
to: contact.get('jid'),
from: X.conn.jid,
type: 'chat'
}).c('body').t(text).tree());
contact.chat.collection.add({
type: 'chat',
cls: 'self',
from: X.conn.jid,
to: contact.get('jid'),
stamp: new Date(),
contact: contacts.findWhere({
type: 'self'
}),
text: text
});
return $('#msg').val('');
}
};
$('[data-send-button]').on('click', sendMessage);
$('#msg').on('keypress', function(e) {
if (e.keyCode === 13) {
return sendMessage();
}
});
$('[data-add-button]').on('click', function() {
var jid;
jid = $('#new-contact').val().trim();
if (jid === '') {
return;
}
if (indexOf.call(jid, '@') < 0) {
jid = jid + "@" + Tram.config.domain;
}
$('#new-contact').val('');
return X.conn.roster.add(jid, null, [], function() {
return X.conn.roster.subscribe(jid, 'I want to chat', null);
});
});
$('#new-contact').on('keypress', function(e) {
if (e.keyCode === 13) {
return $('[data-add-button]').trigger('click');
}
});
window.setInterval(function() {
return $('time[datetime]').each(function() {
var $this;
$this = $(this);
return $this.text(moment($this.attr('datetime')).fromNow());
});
}, 15 * 1000);
}).call(this);
//# sourceMappingURL=index.js.map