78:32a3b275330a
Anton Shestakov <av6@dwimlabs.net>, Sat, 02 Apr 2016 19:11:08 +0800
index: UserState - a new model for tracking local state and ui interactions

next change 79:e0fd1f0f59bf
previous change 76:625459f68893

js/index.js

Permissions: -rw-r--r--

Other formats: Feeds:
// Generated by CoffeeScript 1.10.0
(function() {
var $form, ConnectionData, connectfn, getContact, getContactProfile, getText, getVersion, onConnected, onDisconnected, onGetLast, onGetTime, onGetVersion, onPing, onPresence, onProfileUpdate, onWebRTC,
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.UserState = (function(superClass) {
extend(UserState, superClass);
function UserState() {
return UserState.__super__.constructor.apply(this, arguments);
}
UserState.prototype.defaults = {
show: 'offline'
};
return UserState;
})(Backbone.Model);
if (window.contacts == null) {
window.contacts = new Tram.Contacts();
}
if (window.contactsApp == null) {
window.contactsApp = new Tram.ContactsApp({
el: $('[data-app="contacts"]'),
collection: contacts
});
}
if (window.messages == null) {
window.messages = new Tram.Messages();
}
if (window.logApp == null) {
window.logApp = new Tram.LogApp({
el: $('[data-app="log"]'),
collection: messages
});
}
if (window.calls == null) {
window.calls = new Tram.Calls();
}
if (window.callsApp == null) {
window.callsApp = new Tram.CallsApp({
el: $('[data-app="calls"]'),
collection: calls
});
}
if (window.userState == null) {
window.userState = new Tram.UserState();
}
if (window.faviconApp == null) {
window.faviconApp = new Tram.FaviconApp({
model: userState
});
}
contacts.on('change:show', function(model) {
if (model.get('type') === 'self') {
return userState.set('show', model.get('show'));
}
});
onConnected = function() {
X.conn.addHandler(onPresence, null, 'presence');
X.conn.addHandler(onProfileUpdate, Strophe.NS.VCARD_UPDATE, 'presence');
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 userState.set('show', 'offline');
};
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 delay, name, os, stamp, version;
name = getText(stanza.getElementsByTagName('name')[0]);
version = getText(stanza.getElementsByTagName('version')[0]);
os = getText(stanza.getElementsByTagName('os')[0]);
delay = stanza.getElementsByTagName('delay')[0];
stamp = delay != null ? new Date(delay.getAttribute('stamp')) : new Date();
return messages.add({
id: stanza.getAttribute('id'),
type: stanza.getAttribute('type'),
from: stanza.getAttribute('from'),
stamp: stamp,
cls: 'info',
name: name,
version: version,
os: os,
text: "connected to " + name + " " + version + " on " + 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;
};
getContact = function(from) {
var 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');
});
}
contact = contacts.add({
jid: from,
bjid: Strophe.getBareJidFromJid(from),
type: self ? 'self' : 'contact'
});
contact.on('authorize', function() {
return X.conn.roster.authorize(contact.get('bjid'));
});
contact.on('unauthorize', function() {
X.conn.roster.unauthorize(contact.get('bjid'));
return contacts.remove(contact);
});
contact.on('remove', function() {
return X.conn.roster.remove(contact.get('bjid'), function() {
return contacts.remove(contact);
});
});
contact.w = new Tram.WebRTCInterface(contact);
contact.on('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('accept', function(media) {
return contact.w.init(false, {
audio: indexOf.call(media, 'a') >= 0,
video: indexOf.call(media, 'v') >= 0
});
});
contact.on('decline 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';
switch (type) {
case 'subscribed':
case 'unsubscribe':
case 'unsubscribed':
console.warn("not handling <presence type=\"" + type + "\">", stanza);
return true;
case 'error':
console.error('got <presence type="error">', stanza);
return true;
}
from = stanza.getAttribute('from');
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();
messages.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;
};
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');
messages.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();
});
$('[data-disconnect-button]').on('click', function() {
return X.disconnect('Logged out');
});
$(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');
}
}
});
$('[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