Merge branch 'master' into gh-pages
Conflicts: index.html
This commit is contained in:
commit
4caabf5013
232
Libraries/strophe.disco.js
Normal file
232
Libraries/strophe.disco.js
Normal file
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
Copyright 2010, François de Metz <francois@2metz.fr>
|
||||
*/
|
||||
|
||||
/**
|
||||
* Disco Strophe Plugin
|
||||
* Implement http://xmpp.org/extensions/xep-0030.html
|
||||
* TODO: manage node hierarchies, and node on info request
|
||||
*/
|
||||
Strophe.addConnectionPlugin('disco',
|
||||
{
|
||||
_connection: null,
|
||||
_identities : [],
|
||||
_features : [],
|
||||
_items : [],
|
||||
/** Function: init
|
||||
* Plugin init
|
||||
*
|
||||
* Parameters:
|
||||
* (Strophe.Connection) conn - Strophe connection
|
||||
*/
|
||||
init: function(conn)
|
||||
{
|
||||
this._connection = conn;
|
||||
this._identities = [];
|
||||
this._features = [];
|
||||
this._items = [];
|
||||
// disco info
|
||||
conn.addHandler(this._onDiscoInfo.bind(this), Strophe.NS.DISCO_INFO, 'iq', 'get', null, null);
|
||||
// disco items
|
||||
conn.addHandler(this._onDiscoItems.bind(this), Strophe.NS.DISCO_ITEMS, 'iq', 'get', null, null);
|
||||
},
|
||||
/** Function: addIdentity
|
||||
* See http://xmpp.org/registrar/disco-categories.html
|
||||
* Parameters:
|
||||
* (String) category - category of identity (like client, automation, etc ...)
|
||||
* (String) type - type of identity (like pc, web, bot , etc ...)
|
||||
* (String) name - name of identity in natural language
|
||||
* (String) lang - lang of name parameter
|
||||
*
|
||||
* Returns:
|
||||
* Boolean
|
||||
*/
|
||||
addIdentity: function(category, type, name, lang)
|
||||
{
|
||||
for (var i=0; i<this._identities.length; i++)
|
||||
{
|
||||
if (this._identities[i].category == category &&
|
||||
this._identities[i].type == type &&
|
||||
this._identities[i].name == name &&
|
||||
this._identities[i].lang == lang)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
this._identities.push({category: category, type: type, name: name, lang: lang});
|
||||
return true;
|
||||
},
|
||||
/** Function: addFeature
|
||||
*
|
||||
* Parameters:
|
||||
* (String) var_name - feature name (like jabber:iq:version)
|
||||
*
|
||||
* Returns:
|
||||
* boolean
|
||||
*/
|
||||
addFeature: function(var_name)
|
||||
{
|
||||
for (var i=0; i<this._features.length; i++)
|
||||
{
|
||||
if (this._features[i] == var_name)
|
||||
return false;
|
||||
}
|
||||
this._features.push(var_name);
|
||||
return true;
|
||||
},
|
||||
/** Function: removeFeature
|
||||
*
|
||||
* Parameters:
|
||||
* (String) var_name - feature name (like jabber:iq:version)
|
||||
*
|
||||
* Returns:
|
||||
* boolean
|
||||
*/
|
||||
removeFeature: function(var_name)
|
||||
{
|
||||
for (var i=0; i<this._features.length; i++)
|
||||
{
|
||||
if (this._features[i] === var_name){
|
||||
this._features.splice(i,1)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
/** Function: addItem
|
||||
*
|
||||
* Parameters:
|
||||
* (String) jid
|
||||
* (String) name
|
||||
* (String) node
|
||||
* (Function) call_back
|
||||
*
|
||||
* Returns:
|
||||
* boolean
|
||||
*/
|
||||
addItem: function(jid, name, node, call_back)
|
||||
{
|
||||
if (node && !call_back)
|
||||
return false;
|
||||
this._items.push({jid: jid, name: name, node: node, call_back: call_back});
|
||||
return true;
|
||||
},
|
||||
/** Function: info
|
||||
* Info query
|
||||
*
|
||||
* Parameters:
|
||||
* (Function) call_back
|
||||
* (String) jid
|
||||
* (String) node
|
||||
*/
|
||||
info: function(jid, node, success, error, timeout)
|
||||
{
|
||||
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
|
||||
if (node)
|
||||
attrs.node = node;
|
||||
|
||||
var info = $iq({from:this._connection.jid,
|
||||
to:jid, type:'get'}).c('query', attrs);
|
||||
this._connection.sendIQ(info, success, error, timeout);
|
||||
},
|
||||
/** Function: items
|
||||
* Items query
|
||||
*
|
||||
* Parameters:
|
||||
* (Function) call_back
|
||||
* (String) jid
|
||||
* (String) node
|
||||
*/
|
||||
items: function(jid, node, success, error, timeout)
|
||||
{
|
||||
var attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
|
||||
if (node)
|
||||
attrs.node = node;
|
||||
|
||||
var items = $iq({from:this._connection.jid,
|
||||
to:jid, type:'get'}).c('query', attrs);
|
||||
this._connection.sendIQ(items, success, error, timeout);
|
||||
},
|
||||
|
||||
/** PrivateFunction: _buildIQResult
|
||||
*/
|
||||
_buildIQResult: function(stanza, query_attrs)
|
||||
{
|
||||
var id = stanza.getAttribute('id');
|
||||
var from = stanza.getAttribute('from');
|
||||
var iqresult = $iq({type: 'result', id: id});
|
||||
|
||||
if (from !== null) {
|
||||
iqresult.attrs({to: from});
|
||||
}
|
||||
|
||||
return iqresult.c('query', query_attrs);
|
||||
},
|
||||
|
||||
/** PrivateFunction: _onDiscoInfo
|
||||
* Called when receive info request
|
||||
*/
|
||||
_onDiscoInfo: function(stanza)
|
||||
{
|
||||
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
|
||||
var attrs = {xmlns: Strophe.NS.DISCO_INFO};
|
||||
if (node)
|
||||
{
|
||||
attrs.node = node;
|
||||
}
|
||||
var iqresult = this._buildIQResult(stanza, attrs);
|
||||
for (var i=0; i<this._identities.length; i++)
|
||||
{
|
||||
var attrs = {category: this._identities[i].category,
|
||||
type : this._identities[i].type};
|
||||
if (this._identities[i].name)
|
||||
attrs.name = this._identities[i].name;
|
||||
if (this._identities[i].lang)
|
||||
attrs['xml:lang'] = this._identities[i].lang;
|
||||
iqresult.c('identity', attrs).up();
|
||||
}
|
||||
for (var i=0; i<this._features.length; i++)
|
||||
{
|
||||
iqresult.c('feature', {'var':this._features[i]}).up();
|
||||
}
|
||||
this._connection.send(iqresult.tree());
|
||||
return true;
|
||||
},
|
||||
/** PrivateFunction: _onDiscoItems
|
||||
* Called when receive items request
|
||||
*/
|
||||
_onDiscoItems: function(stanza)
|
||||
{
|
||||
var query_attrs = {xmlns: Strophe.NS.DISCO_ITEMS};
|
||||
var node = stanza.getElementsByTagName('query')[0].getAttribute('node');
|
||||
if (node)
|
||||
{
|
||||
query_attrs.node = node;
|
||||
var items = [];
|
||||
for (var i = 0; i < this._items.length; i++)
|
||||
{
|
||||
if (this._items[i].node == node)
|
||||
{
|
||||
items = this._items[i].call_back(stanza);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var items = this._items;
|
||||
}
|
||||
var iqresult = this._buildIQResult(stanza, query_attrs);
|
||||
for (var i = 0; i < items.length; i++)
|
||||
{
|
||||
var attrs = {jid: items[i].jid};
|
||||
if (items[i].name)
|
||||
attrs.name = items[i].name;
|
||||
if (items[i].node)
|
||||
attrs.node = items[i].node;
|
||||
iqresult.c('item', attrs).up();
|
||||
}
|
||||
this._connection.send(iqresult.tree());
|
||||
return true;
|
||||
}
|
||||
});
|
|
@ -1917,7 +1917,6 @@ Strophe.Handler.prototype = {
|
|||
try {
|
||||
result = this.handler(elem);
|
||||
} catch (e) {
|
||||
result = this.handler(elem);
|
||||
if (e.sourceURL) {
|
||||
Strophe.fatal("error: " + this.handler +
|
||||
" " + e.sourceURL + ":" +
|
||||
|
|
18
converse.css
18
converse.css
|
@ -415,6 +415,19 @@ form.search-xmpp-contact input {
|
|||
margin-top: 0.5em;
|
||||
}
|
||||
|
||||
#available-chatrooms {
|
||||
height: 225px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
#available-chatrooms dd {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
display: inline-block;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
#available-chatrooms dt,
|
||||
#converse-roster dt {
|
||||
font-weight: normal;
|
||||
|
@ -430,11 +443,6 @@ form.search-xmpp-contact input {
|
|||
padding-top: 1em;
|
||||
}
|
||||
|
||||
#available-chatrooms li {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
dd.available-chatroom,
|
||||
#converse-roster dd {
|
||||
font-weight: bold;
|
||||
|
|
124
converse.js
124
converse.js
|
@ -1,8 +1,8 @@
|
|||
/*!
|
||||
* Converse.js (XMPP-based instant messaging with Strophe.js and backbone.js)
|
||||
* Converse.js (Web-based XMPP instant messaging client)
|
||||
* http://conversejs.org
|
||||
*
|
||||
* Copyright (c) 2012 Jan-Carel Brand (jc@opkode.com)
|
||||
* Copyright (c) 2012, Jan-Carel Brand <jc@opkode.com>
|
||||
* Dual licensed under the MIT and GPL Licenses
|
||||
*/
|
||||
|
||||
|
@ -22,7 +22,8 @@
|
|||
"strophe": "Libraries/strophe",
|
||||
"strophe.muc": "Libraries/strophe.muc",
|
||||
"strophe.roster": "Libraries/strophe.roster",
|
||||
"strophe.vcard": "Libraries/strophe.vcard"
|
||||
"strophe.vcard": "Libraries/strophe.vcard",
|
||||
"strophe.disco": "Libraries/strophe.disco"
|
||||
},
|
||||
|
||||
// define module dependencies for modules not using define
|
||||
|
@ -38,22 +39,11 @@
|
|||
//module value.
|
||||
exports: 'Backbone'
|
||||
},
|
||||
|
||||
'underscore': {
|
||||
exports: '_'
|
||||
},
|
||||
|
||||
'strophe.muc': {
|
||||
deps: ['strophe', 'jquery']
|
||||
},
|
||||
|
||||
'strophe.roster': {
|
||||
deps: ['strophe', 'jquery']
|
||||
},
|
||||
|
||||
'strophe.vcard': {
|
||||
deps: ['strophe', 'jquery']
|
||||
}
|
||||
'underscore': { exports: '_' },
|
||||
'strophe.muc': { deps: ['strophe', 'jquery'] },
|
||||
'strophe.roster': { deps: ['strophe', 'jquery'] },
|
||||
'strophe.vcard': { deps: ['strophe', 'jquery'] },
|
||||
'strophe.disco': { deps: ['strophe', 'jquery'] }
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -63,7 +53,8 @@
|
|||
"sjcl",
|
||||
"strophe.muc",
|
||||
"strophe.roster",
|
||||
"strophe.vcard"
|
||||
"strophe.vcard",
|
||||
"strophe.disco"
|
||||
], function() {
|
||||
// Use Mustache style syntax for variable interpolation
|
||||
_.templateSettings = {
|
||||
|
@ -82,7 +73,6 @@
|
|||
root.converse = factory(jQuery, _, console || {log: function(){}});
|
||||
}
|
||||
}(this, function ($, _, console) {
|
||||
|
||||
var converse = {};
|
||||
converse.msg_counter = 0;
|
||||
|
||||
|
@ -728,11 +718,10 @@
|
|||
this.on('update-rooms-list', function (ev) {
|
||||
this.updateRoomsList();
|
||||
});
|
||||
this.trigger('update-rooms-list');
|
||||
},
|
||||
|
||||
updateRoomsList: function () {
|
||||
converse.connection.muc.listRooms(converse.muc_domain, $.proxy(function (iq) {
|
||||
converse.connection.muc.listRooms(this.muc_domain, $.proxy(function (iq) {
|
||||
var name, jid, i,
|
||||
rooms = $(iq).find('query').find('item'),
|
||||
rooms_length = rooms.length,
|
||||
|
@ -744,7 +733,7 @@
|
|||
$available_chatrooms.find('dt').hide();
|
||||
}
|
||||
for (i=0; i<rooms_length; i++) {
|
||||
name = Strophe.unescapeNode($(rooms[i]).attr('name'));
|
||||
name = Strophe.unescapeNode($(rooms[i]).attr('name')||$(rooms[i]).attr('jid'));
|
||||
jid = $(rooms[i]).attr('jid');
|
||||
$available_chatrooms.append(this.room_template({'name':name, 'jid':jid}));
|
||||
}
|
||||
|
@ -762,7 +751,7 @@
|
|||
name = input.val().trim().toLowerCase();
|
||||
input.val(''); // Clear the input
|
||||
if (name) {
|
||||
jid = Strophe.escapeNode(name) + '@' + converse.muc_domain;
|
||||
jid = Strophe.escapeNode(name) + '@' + this.muc_domain;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
@ -790,8 +779,16 @@
|
|||
initialize: function () {
|
||||
this.$el.appendTo(converse.chatboxesview.$el);
|
||||
this.model.on('change', $.proxy(function (item, changed) {
|
||||
var i;
|
||||
if (_.has(item.changed, 'connected')) {
|
||||
this.render();
|
||||
converse.features.on('add', $.proxy(this.featureAdded, this));
|
||||
// Features could have been added before the controlbox was
|
||||
// initialized. Currently we're only interested in MUC
|
||||
var feature = converse.features.findWhere({'var': 'http://jabber.org/protocol/muc'});
|
||||
if (feature) {
|
||||
this.featureAdded(feature);
|
||||
}
|
||||
}
|
||||
if (_.has(item.changed, 'visible')) {
|
||||
if (item.changed.visible === true) {
|
||||
|
@ -799,7 +796,6 @@
|
|||
}
|
||||
}
|
||||
}, this));
|
||||
|
||||
this.model.on('show', this.show, this);
|
||||
this.model.on('destroy', this.hide, this);
|
||||
this.model.on('hide', this.hide, this);
|
||||
|
@ -808,6 +804,17 @@
|
|||
}
|
||||
},
|
||||
|
||||
featureAdded: function (feature) {
|
||||
if (feature.get('var') == 'http://jabber.org/protocol/muc') {
|
||||
if (!this.roomspanel) {
|
||||
this.roomspanel = new converse.RoomsPanel();
|
||||
this.roomspanel.muc_domain = feature.get('from');
|
||||
this.roomspanel.$parent = this.$el;
|
||||
this.roomspanel.render().trigger('update-rooms-list');
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
template: _.template(
|
||||
'<div class="chat-head oc-chat-head">'+
|
||||
'<ul id="controlbox-tabs"></ul>'+
|
||||
|
@ -847,10 +854,6 @@
|
|||
this.contactspanel = new converse.ContactsPanel();
|
||||
this.contactspanel.$parent = this.$el;
|
||||
this.contactspanel.render();
|
||||
// TODO: Only add the rooms panel if the server supports MUC
|
||||
this.roomspanel = new converse.RoomsPanel();
|
||||
this.roomspanel.$parent = this.$el;
|
||||
this.roomspanel.render();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
@ -1842,8 +1845,7 @@
|
|||
}));
|
||||
// iterate through all the <option> elements and add option values
|
||||
options.each(function(){
|
||||
options_list.push(that.option_template({
|
||||
'value': $(this).val(),
|
||||
options_list.push(that.option_template({'value': $(this).val(),
|
||||
'text': $(this).text()
|
||||
}));
|
||||
});
|
||||
|
@ -1854,6 +1856,57 @@
|
|||
}
|
||||
});
|
||||
|
||||
converse.Feature = Backbone.Model.extend();
|
||||
converse.Features = Backbone.Collection.extend({
|
||||
/* This collection stores Feature Models, representing features
|
||||
* provided by available XMPP entities (e.g. servers)
|
||||
*
|
||||
* See XEP-0030 for more details: http://xmpp.org/extensions/xep-0030.html
|
||||
*/
|
||||
model: converse.Feature,
|
||||
initialize: function () {
|
||||
this.localStorage = new Backbone.LocalStorage(
|
||||
hex_sha1('converse.features'+converse.bare_jid));
|
||||
if (this.localStorage.records.length === 0) {
|
||||
// localStorage is empty, so we've likely never queried this
|
||||
// domain for features yet
|
||||
converse.connection.disco.info(converse.domain, null, this.onInfo, this.onError);
|
||||
converse.connection.disco.items(converse.domain, null, $.proxy(this.onItems, this), $.proxy(this.onError, this));
|
||||
} else {
|
||||
this.fetch({add:true});
|
||||
}
|
||||
},
|
||||
|
||||
onItems: function (stanza) {
|
||||
$(stanza).find('query item').each($.proxy(function (idx, item) {
|
||||
converse.connection.disco.info(
|
||||
$(item).attr('jid'),
|
||||
null,
|
||||
$.proxy(this.onInfo, this),
|
||||
$.proxy(this.onError, this));
|
||||
}, this));
|
||||
},
|
||||
|
||||
onInfo: function (stanza) {
|
||||
var $stanza = $(stanza);
|
||||
if (($stanza.find('identity[category=server][type=im]').length === 0) &&
|
||||
($stanza.find('identity[category=conference][type=text]').length === 0)) {
|
||||
// This isn't an IM server component
|
||||
return;
|
||||
}
|
||||
$stanza.find('feature').each($.proxy(function (idx, feature) {
|
||||
this.create({
|
||||
'var': $(feature).attr('var'),
|
||||
'from': $stanza.attr('from')
|
||||
});
|
||||
}, this));
|
||||
},
|
||||
|
||||
onError: function (stanza) {
|
||||
console.log("Error while doing service discovery");
|
||||
}
|
||||
});
|
||||
|
||||
converse.LoginPanel = Backbone.View.extend({
|
||||
tagName: 'div',
|
||||
id: "login-dialog",
|
||||
|
@ -1868,7 +1921,7 @@
|
|||
'<input type="text" id="jid">' +
|
||||
'<label>Password:</label>' +
|
||||
'<input type="password" id="password">' +
|
||||
'<input type="submit" name="submit"/>' +
|
||||
'<button type="submit">Log In</button>' +
|
||||
'</form">'),
|
||||
|
||||
bosh_url_input: _.template(
|
||||
|
@ -1946,6 +1999,7 @@
|
|||
template.find('form').append(this.bosh_url_input);
|
||||
}
|
||||
this.$parent.find('#controlbox-panes').append(this.$el.html(template));
|
||||
this.$el.find('input#jid').focus();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
|
@ -1985,13 +2039,11 @@
|
|||
|
||||
converse.onConnected = function (connection) {
|
||||
this.connection = connection;
|
||||
|
||||
this.animate = true; // Use animations
|
||||
this.connection.xmlInput = function (body) { console.log(body); };
|
||||
this.connection.xmlOutput = function (body) { console.log(body); };
|
||||
this.bare_jid = Strophe.getBareJidFromJid(this.connection.jid);
|
||||
this.domain = Strophe.getDomainFromJid(this.connection.jid);
|
||||
this.muc_domain = 'conference.' + this.domain;
|
||||
this.features = new this.Features();
|
||||
|
||||
// Set up the roster
|
||||
this.roster = new this.RosterItems();
|
||||
|
|
17
index.html
17
index.html
|
@ -43,18 +43,19 @@
|
|||
|
||||
<h2>Features</h2>
|
||||
<ul>
|
||||
<li>Single and multi-user chat</li>
|
||||
<li>Single-user chat</li>
|
||||
<li>Multi-user chat in chatrooms (<a href="http://xmpp.org/extensions/xep-0045.html">XEP 45</a>)</li>
|
||||
<li>vCard support (<a href="http://xmpp.org/extensions/xep-0054.html">XEP 54</a>)</li>
|
||||
<li>Service discovery (<a href="http://xmpp.org/extensions/xep-0030.html">XEP 30</a>)</li>
|
||||
<li>Contact rosters</li>
|
||||
<li>Manually or automically subscribe to other contacts</li>
|
||||
<li>Roster item exchange (<a href="http://xmpp.org/extensions/tmp/xep-0144-1.1.html">XEP 144</a>)</li>
|
||||
<li>Accept or decline contact requests</li>
|
||||
<li>Roster item exchange (<a href="http://xmpp.org/extensions/tmp/xep-0144-1.1.html">XEP 144</a>)</li>
|
||||
<li>Chat statuses (online, busy, away, offline)</li>
|
||||
<li>Custom status messages</li>
|
||||
<li>Typing notifications</li>
|
||||
<li>Third person messages (/me )</li>
|
||||
<li>Multi-user chat in chatrooms (<a href="http://xmpp.org/extensions/xep-0045.html">XEP 45</a>)</li>
|
||||
<li>Chatroom Topics</li>
|
||||
<li>vCard support (<a href="http://xmpp.org/extensions/xep-0054.html">XEP 54</a>)</li>
|
||||
</ul>
|
||||
|
||||
<h2>CMS Integration</h2>
|
||||
|
@ -76,8 +77,7 @@
|
|||
|
||||
<h2>Demo</h2>
|
||||
<p><a href="#" class="chat toggle-online-users">Click this link</a> or click the link on the bottom right corner of this page.</a></p>
|
||||
<p>
|
||||
You can log in with any existing federated Jabber/XMPP account, or create a new one at any of these providers:
|
||||
<p>You can log in with any existing federated Jabber/XMPP account, or create a new one at any of these providers:
|
||||
<ul>
|
||||
<li><a href="http://jabber.org" target="_blank">jabber.org</a></li>
|
||||
<li><a href="https://jappix.com" target="_blank">jappix.com</a></li>
|
||||
|
@ -98,10 +98,7 @@
|
|||
establish an authenticated connection on the server side and then attach to
|
||||
this connection in your browser.
|
||||
</p>
|
||||
<p><strong>Converse.js</strong> already supports this usecase, but you'll have to
|
||||
do more manual work yourself.
|
||||
</p>
|
||||
|
||||
<p><strong>Converse.js</strong> already supports this usecase, but you'll have to do more manual work yourself.</p>
|
||||
|
||||
<h2>Tests</h2>
|
||||
</p>
|
||||
|
|
Loading…
Reference in New Issue
Block a user