Browse Source

Merge branch 'master' into mockup

Conflicts:
	converse.css
	mockup.html
JC Brand 12 years ago
parent
commit
dfe76e42f0

+ 1 - 1
.travis.yml

@@ -2,5 +2,5 @@ language: node_js
 node_js:
 node_js:
  - "0.10"
  - "0.10"
 before_script:
 before_script:
- - "./node_modules/.bin/bower update"
+ - "./node_modules/.bin/grunt fetch"
 script: grunt test
 script: grunt test

+ 7 - 1
CHANGES.rst

@@ -1,12 +1,18 @@
 Changelog
 Changelog
 =========
 =========
 
 
-0.5.3 (Unreleased)
+0.6.0 (Unreleased)
 ------------------
 ------------------
 
 
+- #39 Documentation for minifying JS is wrong. [jcbrand]
+- #41 prebind and show_controlbox_by_default true fails. [jcbrand]
+- With prebinding, attaching to the connection now happens inside Converse and
+  not as a separate step after initialization. [jcbrand]
 - Register presence and message handlers before fetching the roster. Otherwise
 - Register presence and message handlers before fetching the roster. Otherwise
   some presence notifications might be missed. [jcbrand]
   some presence notifications might be missed. [jcbrand]
 - Add a debug option (logs to the browser console). [jcbrand]
 - Add a debug option (logs to the browser console). [jcbrand]
+- Use font icons from http://icomoon.io [jcbrand]
+- Added a static mockup to aid CSS/design process. [jcbrand]
 
 
 0.5.2 (2013-08-05)
 0.5.2 (2013-08-05)
 ------------------
 ------------------

+ 2 - 2
Gruntfile.js

@@ -95,7 +95,7 @@ module.exports = function(grunt) {
         var done = this.async();
         var done = this.async();
         var child_process = require('child_process');
         var child_process = require('child_process');
         var exec = child_process.exec;
         var exec = child_process.exec;
-        exec('bower update && cd ./components/strophe && make normal',
+        exec('./node_modules/.bin/bower update && cd ./components/strophe && make normal',
              function (err, stdout, stderr) {
              function (err, stdout, stderr) {
                 if (err) {
                 if (err) {
                     grunt.log.write('build failed with error code '+err.code);
                     grunt.log.write('build failed with error code '+err.code);
@@ -106,7 +106,7 @@ module.exports = function(grunt) {
         });
         });
     });
     });
 
 
-    grunt.registerTask('minify', 'Create a new release', ['requirejs', 'cssmin']);
+    grunt.registerTask('minify', 'Create a new release', ['cssmin', 'requirejs']);
 
 
     grunt.registerTask('check', 'Perform all checks (e.g. before releasing)', function () {
     grunt.registerTask('check', 'Perform all checks (e.g. before releasing)', function () {
         grunt.task.run('jshint', 'test');
         grunt.task.run('jshint', 'test');

+ 3 - 0
README.rst

@@ -2,6 +2,9 @@
 converse.js
 converse.js
 ===========
 ===========
 
 
+.. figure:: https://api.travis-ci.org/jcbrand/converse.js.png?branch=master
+   :alt: Build Status
+
 Converse.js_ is a web based `XMPP/Jabber`_ instant messaging client.
 Converse.js_ is a web based `XMPP/Jabber`_ instant messaging client.
 
 
 It enables you to add chat functionality to your website, independent of any
 It enables you to add chat functionality to your website, independent of any

+ 1 - 1
bower.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "converse",
   "name": "converse",
-  "version": "0.5.2",
+  "version": "0.6.0",
   "devDependencies": {
   "devDependencies": {
     "jasmine": "https://github.com/jcbrand/jasmine.git#1_3_x"
     "jasmine": "https://github.com/jcbrand/jasmine.git#1_3_x"
   },
   },

+ 10 - 1
converse.css

@@ -110,11 +110,11 @@ span.spinner.hor_centered {
 
 
 .chatroom .participants {
 .chatroom .participants {
     float: left;
     float: left;
-    width: auto;
     height: 272px;
     height: 272px;
     background-color: white;
     background-color: white;
     overflow: auto;
     overflow: auto;
     border-left: 1px solid #AAA;
     border-left: 1px solid #AAA;
+    max-width: 99px;
 }
 }
 
 
 .participants ul.participant-list li {
 .participants ul.participant-list li {
@@ -152,6 +152,7 @@ ul.participant-list li.moderator {
     font-size: 13px;
     font-size: 13px;
     color: rgb(79, 79, 79);
     color: rgb(79, 79, 79);
     height:173px;
     height:173px;
+    width: 190px;
     overflow-y:auto;
     overflow-y:auto;
     border: 0;
     border: 0;
     background-color: #ffffff;
     background-color: #ffffff;
@@ -175,6 +176,10 @@ ul.participant-list li.moderator {
     padding-right: 3px;
     padding-right: 3px;
 }
 }
 
 
+.chat-message-content {
+    word-wrap: break-word;
+}
+
 .chat-message-them {
 .chat-message-them {
     color: #F62817;
     color: #F62817;
 }
 }
@@ -577,6 +582,10 @@ dd.available-chatroom:hover a.room-info {
     border-radius: 4px;
     border-radius: 4px;
 }
 }
 
 
+.chatbox {
+    width: 200px;
+}
+
 .chatroom {
 .chatroom {
     width: 300px;
     width: 300px;
 }
 }

+ 121 - 75
converse.js

@@ -41,20 +41,40 @@
     }
     }
 }(this, function ($, _, console) {
 }(this, function ($, _, console) {
     var converse = {};
     var converse = {};
-    converse.initialize = function (settings) {
+    converse.initialize = function (settings, callback) {
         // Default values
         // Default values
         var converse = this;
         var converse = this;
         this.animate = true;
         this.animate = true;
         this.auto_list_rooms = false;
         this.auto_list_rooms = false;
         this.auto_subscribe = false;
         this.auto_subscribe = false;
-        this.bosh_service_url = ''; // The BOSH connection manager URL. Required if you are not prebinding.
+        this.bosh_service_url = undefined; // The BOSH connection manager URL.
         this.debug = false;
         this.debug = false;
         this.hide_muc_server = false;
         this.hide_muc_server = false;
         this.i18n = locales.en;
         this.i18n = locales.en;
         this.prebind = false;
         this.prebind = false;
         this.show_controlbox_by_default = false;
         this.show_controlbox_by_default = false;
         this.xhr_user_search = false;
         this.xhr_user_search = false;
-        _.extend(this, settings);
+        this.testing = false; // Exposes sensitive data for testing. Never set to true in production systems!
+        this.callback = callback || function () {};
+
+        // Allow only the whitelisted settings attributes to be overwritten,
+        // nothing else.
+        whitelist = [
+            'animate',
+            'auto_list_rooms',
+            'auto_subscribe',
+            'bosh_service_url',
+            'fullname',
+            'debug',
+            'hide_muc_server',
+            'i18n',
+            'prebind',
+            'show_controlbox_by_default',
+            'xhr_user_search',
+            'connection',
+            'testing'
+        ];
+        _.extend(this, _.pick(settings, whitelist));
 
 
         var __ = $.proxy(function (str) {
         var __ = $.proxy(function (str) {
             var t = this.i18n.translate(str);
             var t = this.i18n.translate(str);
@@ -71,6 +91,49 @@
             return text.replace(re, '<a target="_blank" href="$1">$1</a>');
             return text.replace(re, '<a target="_blank" href="$1">$1</a>');
         };
         };
 
 
+        this.giveFeedback = function (message, klass) {
+            $('.conn-feedback').text(message);
+            $('.conn-feedback').attr('class', 'conn-feedback');
+            if (klass) {
+                $('.conn-feedback').addClass(klass);
+            }
+        };
+
+        this.log = function (txt) {
+            if (this.debug) {
+                console.log(txt);
+            }
+        };
+
+        this.onConnect = function (status) {
+            if (status === Strophe.Status.CONNECTED) {
+                converse.log('Connected');
+                converse.onConnected();
+            } else if (status === Strophe.Status.DISCONNECTED) {
+                if ($button) { $button.show().siblings('span').remove(); }
+                converse.giveFeedback(__('Disconnected'), 'error');
+                converse.connection.connect(connection.jid, connection.pass, converse.onConnect);
+            } else if (status === Strophe.Status.Error) {
+                if ($button) { $button.show().siblings('span').remove(); }
+                converse.giveFeedback(__('Error'), 'error');
+            } else if (status === Strophe.Status.CONNECTING) {
+                converse.giveFeedback(__('Connecting'));
+            } else if (status === Strophe.Status.CONNFAIL) {
+                converse.chatboxesview.views.controlbox.trigger('connection-fail');
+                converse.giveFeedback(__('Connection Failed'), 'error');
+            } else if (status === Strophe.Status.AUTHENTICATING) {
+                converse.giveFeedback(__('Authenticating'));
+            } else if (status === Strophe.Status.AUTHFAIL) {
+                converse.chatboxesview.views.controlbox.trigger('auth-fail');
+                converse.giveFeedback(__('Authentication Failed'), 'error');
+            } else if (status === Strophe.Status.DISCONNECTING) {
+                converse.giveFeedback(__('Disconnecting'), 'error');
+            } else if (status === Strophe.Status.ATTACHED) {
+                converse.log('Attached');
+                converse.onConnected();
+            }
+        };
+
         this.toISOString = function (date) {
         this.toISOString = function (date) {
             var pad;
             var pad;
             if (typeof date.toISOString !== 'undefined') {
             if (typeof date.toISOString !== 'undefined') {
@@ -157,10 +220,10 @@
                         .t('1');
                         .t('1');
 
 
             converse.connection.sendIQ(iq,
             converse.connection.sendIQ(iq,
-                        callback,
-                        function () {
-                            console.log('Error while retrieving collections');
-                        });
+                callback,
+                function () {
+                    converse.log('Error while retrieving collections');
+                });
         };
         };
 
 
         this.collections.getLastMessages = function (jid, callback) {
         this.collections.getLastMessages = function (jid, callback) {
@@ -484,7 +547,7 @@
                             });
                             });
                         }, this),
                         }, this),
                         $.proxy(function (stanza) {
                         $.proxy(function (stanza) {
-                            console.log("ChatBoxView.initialize: An error occured while fetching vcard");
+                            converse.log("ChatBoxView.initialize: An error occured while fetching vcard");
                         }, this)
                         }, this)
                     );
                     );
                 }
                 }
@@ -696,7 +759,7 @@
                         this.addContact(jid, fullname);
                         this.addContact(jid, fullname);
                     }, this),
                     }, this),
                     $.proxy(function (stanza) {
                     $.proxy(function (stanza) {
-                        console.log("An error occured while fetching vcard");
+                        converse.log("An error occured while fetching vcard");
                         var jid = $(stanza).attr('from');
                         var jid = $(stanza).attr('from');
                         this.addContact(jid, jid);
                         this.addContact(jid, jid);
                     }, this));
                     }, this));
@@ -943,7 +1006,7 @@
                     }
                     }
                 }
                 }
                 if (!nick) { return; }
                 if (!nick) { return; }
-                chatroom = converse.chatboxes.createChatBox({
+                chatroom = converse.chatboxesview.showChatBox({
                     'id': jid,
                     'id': jid,
                     'jid': jid,
                     'jid': jid,
                     'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
                     'name': Strophe.unescapeNode(Strophe.getNodeFromJid(jid)),
@@ -1039,7 +1102,7 @@
                 if ((!converse.prebind) && (!converse.connection)) {
                 if ((!converse.prebind) && (!converse.connection)) {
                     // Add login panel if the user still has to authenticate
                     // Add login panel if the user still has to authenticate
                     this.$el.html(this.template(this.model.toJSON()));
                     this.$el.html(this.template(this.model.toJSON()));
-                    this.loginpanel = new converse.LoginPanel({'$parent': this.$el.find('#controlbox-panes')});
+                    this.loginpanel = new converse.LoginPanel({'$parent': this.$el.find('#controlbox-panes'), 'model': this});
                     this.loginpanel.render();
                     this.loginpanel.render();
                 } else if (!this.contactspanel) {
                 } else if (!this.contactspanel) {
                     this.$el.html(this.template(this.model.toJSON()));
                     this.$el.html(this.template(this.model.toJSON()));
@@ -1609,16 +1672,6 @@
                 });
                 });
             },
             },
 
 
-            createChatBox: function (attrs) {
-                var chatbox  = this.get(attrs.jid);
-                if (chatbox) {
-                    chatbox.trigger('show');
-                } else {
-                    chatbox = this.create(attrs);
-                }
-                return chatbox;
-            },
-
             messageReceived: function (message) {
             messageReceived: function (message) {
                 var partner_jid, $message = $(message),
                 var partner_jid, $message = $(message),
                     message_from = $message.attr('from');
                     message_from = $message.attr('from');
@@ -1688,6 +1741,20 @@
                         }
                         }
                     }
                     }
                 }, this);
                 }, this);
+            },
+
+            showChatBox: function (attrs) {
+                var chatbox  = this.model.get(attrs.jid);
+                if (chatbox) {
+                    chatbox.trigger('show');
+                } else {
+                    chatbox = this.model.create(attrs, {
+                        'error': function (model, response) {
+                            converse.log(response.responseText);
+                        }
+                    });
+                }
+                return chatbox;
             }
             }
         });
         });
 
 
@@ -1721,7 +1788,7 @@
 
 
             openChat: function (ev) {
             openChat: function (ev) {
                 ev.preventDefault();
                 ev.preventDefault();
-                converse.chatboxes.createChatBox({
+                converse.chatboxesview.showChatBox({
                     'id': this.model.get('jid'),
                     'id': this.model.get('jid'),
                     'jid': this.model.get('jid'),
                     'jid': this.model.get('jid'),
                     'fullname': this.model.get('fullname'),
                     'fullname': this.model.get('fullname'),
@@ -2063,7 +2130,7 @@
                                     });
                                     });
                                 }, this),
                                 }, this),
                                 $.proxy(function (jid, fullname, img, img_type, url) {
                                 $.proxy(function (jid, fullname, img, img_type, url) {
-                                    console.log("Error while retrieving vcard");
+                                    converse.log("Error while retrieving vcard");
                                     this.add({jid: bare_jid, subscription: 'none', ask: 'request', fullname: jid, is_last: true});
                                     this.add({jid: bare_jid, subscription: 'none', ask: 'request', fullname: jid, is_last: true});
                                 }, this)
                                 }, this)
                             );
                             );
@@ -2485,44 +2552,26 @@
                 '<input type="text" id="bosh_service_url">'),
                 '<input type="text" id="bosh_service_url">'),
 
 
             connect: function ($form, jid, password) {
             connect: function ($form, jid, password) {
-                var button = null,
-                    connection = new Strophe.Connection(converse.bosh_service_url);
                 if ($form) {
                 if ($form) {
-                    $button = $form.find('input[type=submit]');
-                    $button.hide().after('<span class="spinner login-submit"/>');
-                }
-                connection.connect(jid, password, $.proxy(function (status, message) {
-                    if (status === Strophe.Status.CONNECTED) {
-                        console.log(__('Connected'));
-                        converse.onConnected(connection);
-                    } else if (status === Strophe.Status.DISCONNECTED) {
-                        if ($button) { $button.show().siblings('span').remove(); }
-                        converse.giveFeedback(__('Disconnected'), 'error');
-                        this.connect(null, connection.jid, connection.pass);
-                    } else if (status === Strophe.Status.Error) {
-                        if ($button) { $button.show().siblings('span').remove(); }
-                        converse.giveFeedback(__('Error'), 'error');
-                    } else if (status === Strophe.Status.CONNECTING) {
-                        converse.giveFeedback(__('Connecting'));
-                    } else if (status === Strophe.Status.CONNFAIL) {
-                        if ($button) { $button.show().siblings('span').remove(); }
-                        converse.giveFeedback(__('Connection Failed'), 'error');
-                    } else if (status === Strophe.Status.AUTHENTICATING) {
-                        converse.giveFeedback(__('Authenticating'));
-                    } else if (status === Strophe.Status.AUTHFAIL) {
-                        if ($button) { $button.show().siblings('span').remove(); }
-                        converse.giveFeedback(__('Authentication Failed'), 'error');
-                    } else if (status === Strophe.Status.DISCONNECTING) {
-                        converse.giveFeedback(__('Disconnecting'), 'error');
-                    } else if (status === Strophe.Status.ATTACHED) {
-                        console.log(__('Attached'));
-                    }
-                }, this));
+                    $form.find('input[type=submit]').hide().after('<span class="spinner login-submit"/>');
+                }
+                converse.connection = new Strophe.Connection(converse.bosh_service_url);
+                converse.connection.connect(jid, password, converse.onConnect);
+            },
+
+            showConnectButton: function () {
+                var $form = this.$el.find('#converse-login');
+                var $button = $form.find('input[type=submit]')
+                if ($button.length) {
+                    $button.show().siblings('span').remove();
+                }
             },
             },
 
 
             initialize: function (cfg) {
             initialize: function (cfg) {
                 cfg.$parent.append(this.$el.html(this.template()));
                 cfg.$parent.append(this.$el.html(this.template()));
                 this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
                 this.$tabs = cfg.$parent.parent().find('#controlbox-tabs');
+                this.model.on('connection-fail', function () { this.showConnectButton(); }, this);
+                this.model.on('auth-fail', function () { this.showConnectButton(); }, this);
             },
             },
 
 
             render: function () {
             render: function () {
@@ -2596,14 +2645,6 @@
             }
             }
         };
         };
 
 
-        this.giveFeedback = function (message, klass) {
-            $('.conn-feedback').text(message);
-            $('.conn-feedback').attr('class', 'conn-feedback');
-            if (klass) {
-                $('.conn-feedback').addClass(klass);
-            }
-        };
-
         this.initStatus = function (callback) {
         this.initStatus = function (callback) {
             this.xmppstatus = new this.XMPPStatus();
             this.xmppstatus = new this.XMPPStatus();
             var id = hex_sha1('converse.xmppstatus-'+this.bare_jid);
             var id = hex_sha1('converse.xmppstatus-'+this.bare_jid);
@@ -2623,8 +2664,7 @@
             this.rosterview = new this.RosterView({'model':this.roster});
             this.rosterview = new this.RosterView({'model':this.roster});
         }
         }
 
 
-        this.onConnected = function (connection, callback) {
-            this.connection = connection;
+        this.onConnected = function () {
             if (this.debug) {
             if (this.debug) {
                 this.connection.xmlInput = function (body) { console.log(body); };
                 this.connection.xmlInput = function (body) { console.log(body); };
                 this.connection.xmlOutput = function (body) { console.log(body); };
                 this.connection.xmlOutput = function (body) { console.log(body); };
@@ -2663,8 +2703,10 @@
                     this.windowState = e.type;
                     this.windowState = e.type;
                 },this));
                 },this));
                 this.giveFeedback(__('Online Contacts'));
                 this.giveFeedback(__('Online Contacts'));
-                if (callback) {
-                    callback(this);
+                if (this.testing) {
+                    this.callback(this);
+                } else  {
+                    this.callback();
                 }
                 }
             }, this));
             }, this));
         };
         };
@@ -2678,17 +2720,21 @@
                 e.preventDefault(); this.toggleControlBox();
                 e.preventDefault(); this.toggleControlBox();
             }, this)
             }, this)
         );
         );
-        if (this.show_controlbox_by_default) {
-            this.toggleControlBox();
+        if ((this.prebind) && (!this.connection)) {
+            if ((!this.jid) || (!this.sid) || (!this.rid) || (!this.bosh_service_url)) {
+                this.log('If you set prebind=true, you MUST supply JID, RID and SID values');
+                return;
+            }
+            this.connection = new Strophe.Connection(this.bosh_service_url);
+            this.connection.attach(this.jid, this.sid, this.rid, this.onConnect);
+        } else if (this.connection) {
+            this.onConnected();
         }
         }
+        if (this.show_controlbox_by_default) { this.showControlBox(); }
     };
     };
     return {
     return {
-        'initialize': function (settings) {
-            converse.initialize(settings);
-        },
-        'onConnected': function (connection, callback) { 
-            // onConnected can only be called after initialize has been called.
-            converse.onConnected(connection, callback);
+        'initialize': function (settings, callback) {
+            converse.initialize(settings, callback);
         }
         }
     };
     };
 }));
 }));

File diff suppressed because it is too large
+ 0 - 0
converse.min.js


BIN
docs/doctrees/index.doctree


+ 1 - 1
docs/html/.buildinfo

@@ -1,4 +1,4 @@
 # Sphinx build info version 1
 # Sphinx build info version 1
 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
 # This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
-config: 75b256f63edf933c138244d36b1fd86c
+config: cc4e774ae6c30c8f75f9540598d47e5a
 tags: fbb0d17656682115ca4d033fb2f83ba1
 tags: fbb0d17656682115ca4d033fb2f83ba1

+ 44 - 58
docs/html/_sources/index.txt

@@ -188,15 +188,17 @@ Jack Moffitt has a great `blogpost`_ about this and even provides an `example Dj
 .. Note::
 .. Note::
    If you want to enable single session support, make sure to pass **prebind: true**
    If you want to enable single session support, make sure to pass **prebind: true**
    when you call **converse.initialize** (see ./index.html).
    when you call **converse.initialize** (see ./index.html).
+   Additionally you need to pass in valid **jid**, **sid**, **rid** and
+   **bosh_service_url** values.
 
 
 When you authenticate to the XMPP server on your backend, you'll receive two
 When you authenticate to the XMPP server on your backend, you'll receive two
 tokens, RID (request ID) and SID (session ID).
 tokens, RID (request ID) and SID (session ID).
 
 
 These tokens then need to be passed back to the javascript running in your
 These tokens then need to be passed back to the javascript running in your
-browser, where you will need them attach to the existing session.
+browser, where you will need them to attach to the existing session.
 
 
 You can embed the RID and SID tokens in your HTML markup or you can do an
 You can embed the RID and SID tokens in your HTML markup or you can do an
-XMLHttpRequest call to you server and ask it to return them for you.
+XMLHttpRequest call to your server and ask it to return them for you.
 
 
 Below is one example of how this could work. An Ajax call is made to the
 Below is one example of how this could work. An Ajax call is made to the
 relative URL **/prebind** and it expects to receive JSON data back.
 relative URL **/prebind** and it expects to receive JSON data back.
@@ -204,26 +206,19 @@ relative URL **/prebind** and it expects to receive JSON data back.
 :: 
 :: 
 
 
     $.getJSON('/prebind', function (data) {
     $.getJSON('/prebind', function (data) {
-            var connection = new Strophe.Connection(converse.bosh_service_url);
-            connection.attach(data.jid, data.sid, data.rid, function (status) {
-                if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED)) {
-                    converse.onConnected(connection)
-                } 
-            });
-        }
+        converse.initialize({
+            prebind: true,
+            bosh_service_url: data.bosh_service_url,
+            jid: data.jid,
+            sid: data.sid,
+            rid: data.rid
+        });
     );
     );
 
 
 **Here's what's happening:**
 **Here's what's happening:**
 
 
-The JSON data contains the user's JID (jabber ID), RID and SID. The URL to the
-BOSH connection manager is already set as a configuration setting on the
-*converse* object (see ./main.js), so we can reuse it from there.
-
-A new Strophe.Connection object is instantiated and then *attach* is called with
-the user's JID, the necessary tokens and a callback function.
-
-In the callback function, you call *converse.onConnected* together with the
-connection object.
+The JSON data contains the user's JID (jabber ID), RID, SID and the URL to the
+BOSH connection manager. 
 
 
 
 
 Facebook integration
 Facebook integration
@@ -463,25 +458,36 @@ a middle man between HTTP and XMPP.
 
 
 See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
 See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
 
 
+debug
+-----
+
+If set to true, debugging output will be logged to the browser console.
+
 fullname
 fullname
 --------
 --------
 
 
-If you are using prebinding, you need to specify the fullname of the currently
-logged in user.
+If you are using prebinding, can specify the fullname of the currently
+logged in user, otherwise the user's vCard will be fetched.
 
 
 hide_muc_server
 hide_muc_server
 ---------------
 ---------------
 
 
-Default = False
+Default = false
 
 
 Hide the ``server`` input field of the form inside the ``Room`` panel of the
 Hide the ``server`` input field of the form inside the ``Room`` panel of the
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 your choosing.
 your choosing.
 
 
+i18n
+----
+
+Specify the locale/language. The language must be in the ``locales`` object. Refer to
+``./locale/locales.js`` to see which locales are supported.
+
 prebind
 prebind
 --------
 --------
 
 
-Default = False
+Default = false
 
 
 Use this option when you want to attach to an existing XMPP connection that was
 Use this option when you want to attach to an existing XMPP connection that was
 already authenticated (usually on the backend before page load).
 already authenticated (usually on the backend before page load).
@@ -489,26 +495,19 @@ already authenticated (usually on the backend before page load).
 This is useful when you don't want to render the login form on the chat control
 This is useful when you don't want to render the login form on the chat control
 box with each page load.
 box with each page load.
 
 
-When set to true, you'll need to make sure that the onConnected method is 
-called, and passed to it a Strophe connection object.
+For prebinding to work, your backend server must authenticate for you, and 
+then return a JID (jabber ID), SID (session ID) and RID (Request ID).
 
 
-Besides requiring the back-end to authenticate you, you'll also 
-have to write a Javascript snippet to attach to the set up connection::
+If you set ``prebind`` to ``true``, you have to make sure to also pass in these
+values as ``jid``, ``sid``, ``rid``.
 
 
-    $.JSON({
-        'url': 'mysite.com/xmpp-authenticate',
-        'success': function (data) {
-            connection = new Strophe.Connection(bosh_service_url);
-            connection.attach(data.jid, data.sid, data.rid, converse.onConnected);
-        }
+Additionally, you have to specify ``bosh_service_url``.
 
 
-The backend must authenticate for you, and then return a SID (session ID) and
-RID (Request ID), which you use when you attach to the connection.
 
 
 show_controlbox_by_default
 show_controlbox_by_default
 --------------------------
 --------------------------
 
 
-Default = False
+Default = false
 
 
 The "controlbox" refers to the special chatbox containing your contacts roster,
 The "controlbox" refers to the special chatbox containing your contacts roster,
 status widget, chatrooms and other controls.
 status widget, chatrooms and other controls.
@@ -537,40 +536,27 @@ be used.
 Minification
 Minification
 ============
 ============
 
 
-Minifying Javascript
-====================
+Minifying Javascript and CSS
+============================
+
+Please make sure to read the section `Development`_ and that you have installed
+all development dependencies (long story short, you can run ``npm install``
+and then ``grunt fetch``).
 
 
 We  use `require.js`_ to keep track of *Converse.js* and its dependencies and to
 We  use `require.js`_ to keep track of *Converse.js* and its dependencies and to
 to bundle them together in a single minified file fit for deployment to a 
 to bundle them together in a single minified file fit for deployment to a 
 production site.
 production site.
 
 
-To use the require.js's optimization tool, you'll need Node and it's package
-manager, NPM.
-
-You can then install install require.js for Node like so:
-
-::
-
-    npm install requirejs
-
-The minified javascript file is then created like this:
+To minify the Javascript and CSS, run the following command:
 
 
 ::
 ::
 
 
-    r.js -o build.js
-
-You should now have a new minified file (the name which is specified in build.js).
+    grunt minify
 
 
+Javascript will be bundled and minified via `require.js`_'s optimization tool.
 You can `read more about require.js's optimizer here`_.
 You can `read more about require.js's optimizer here`_.
 
 
-Minifying CSS
-=============
-
-CSS can be minimized with Yahoo's yuicompressor tool:
-
-::
-
-    yui-compressor --type=css converse.css -o converse.min.css
+CSS is minified via `cssmin <https://github.com/gruntjs/grunt-contrib-cssmin>`_.
 
 
 
 
 ============
 ============

+ 7 - 0
docs/html/_static/stylesheet.css

@@ -32,4 +32,11 @@ h1 a {
 
 
 ul {
 ul {
     margin-bottom: 5px;
     margin-bottom: 5px;
+}
+
+tt.literal {
+    color: #222;
+    background-color: #fff;
+    font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
+    font-size: 14px;
 }
 }

+ 5 - 5
docs/html/genindex.html

@@ -9,7 +9,7 @@
   <head>
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     
     
-    <title>Index &mdash; Converse.js 0.5.1 documentation</title>
+    <title>Index &mdash; Converse.js 0.5.3 documentation</title>
     
     
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@@ -17,7 +17,7 @@
     <script type="text/javascript">
     <script type="text/javascript">
       var DOCUMENTATION_OPTIONS = {
       var DOCUMENTATION_OPTIONS = {
         URL_ROOT:    '',
         URL_ROOT:    '',
-        VERSION:     '0.5.1',
+        VERSION:     '0.5.3',
         COLLAPSE_INDEX: false,
         COLLAPSE_INDEX: false,
         FILE_SUFFIX: '.html',
         FILE_SUFFIX: '.html',
         HAS_SOURCE:  true
         HAS_SOURCE:  true
@@ -26,7 +26,7 @@
     <script type="text/javascript" src="_static/jquery.js"></script>
     <script type="text/javascript" src="_static/jquery.js"></script>
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
-    <link rel="top" title="Converse.js 0.5.1 documentation" href="index.html" /> 
+    <link rel="top" title="Converse.js 0.5.3 documentation" href="index.html" /> 
   </head>
   </head>
   <body>
   <body>
     <div id="header_wrap" class="outer">
     <div id="header_wrap" class="outer">
@@ -51,7 +51,7 @@
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="#" title="General Index"
           <a href="#" title="General Index"
              accesskey="I">index</a></li>
              accesskey="I">index</a></li>
-        <li><a href="index.html">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="index.html">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 <section id="main_content" class="inner">  
 <section id="main_content" class="inner">  
@@ -80,7 +80,7 @@
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="#" title="General Index"
           <a href="#" title="General Index"
              >index</a></li>
              >index</a></li>
-        <li><a href="index.html">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="index.html">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 </div>
 </div>

+ 65 - 73
docs/html/index.html

@@ -7,7 +7,7 @@
   <head>
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     
     
-    <title>Quickstart (to get a demo up and running) &mdash; Converse.js 0.5.1 documentation</title>
+    <title>Quickstart (to get a demo up and running) &mdash; Converse.js 0.5.3 documentation</title>
     
     
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@@ -15,7 +15,7 @@
     <script type="text/javascript">
     <script type="text/javascript">
       var DOCUMENTATION_OPTIONS = {
       var DOCUMENTATION_OPTIONS = {
         URL_ROOT:    '',
         URL_ROOT:    '',
-        VERSION:     '0.5.1',
+        VERSION:     '0.5.3',
         COLLAPSE_INDEX: false,
         COLLAPSE_INDEX: false,
         FILE_SUFFIX: '.html',
         FILE_SUFFIX: '.html',
         HAS_SOURCE:  true
         HAS_SOURCE:  true
@@ -24,7 +24,7 @@
     <script type="text/javascript" src="_static/jquery.js"></script>
     <script type="text/javascript" src="_static/jquery.js"></script>
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
-    <link rel="top" title="Converse.js 0.5.1 documentation" href="#" /> 
+    <link rel="top" title="Converse.js 0.5.3 documentation" href="#" /> 
   </head>
   </head>
   <body>
   <body>
     <div id="header_wrap" class="outer">
     <div id="header_wrap" class="outer">
@@ -49,7 +49,7 @@
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="genindex.html" title="General Index"
           <a href="genindex.html" title="General Index"
              accesskey="I">index</a></li>
              accesskey="I">index</a></li>
-        <li><a href="#">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="#">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 <section id="main_content" class="inner">  
 <section id="main_content" class="inner">  
@@ -99,21 +99,22 @@
 <li><a class="reference internal" href="#auto-list-rooms" id="id23">auto_list_rooms</a></li>
 <li><a class="reference internal" href="#auto-list-rooms" id="id23">auto_list_rooms</a></li>
 <li><a class="reference internal" href="#auto-subscribe" id="id24">auto_subscribe</a></li>
 <li><a class="reference internal" href="#auto-subscribe" id="id24">auto_subscribe</a></li>
 <li><a class="reference internal" href="#bosh-service-url" id="id25">bosh_service_url</a></li>
 <li><a class="reference internal" href="#bosh-service-url" id="id25">bosh_service_url</a></li>
-<li><a class="reference internal" href="#fullname" id="id26">fullname</a></li>
-<li><a class="reference internal" href="#hide-muc-server" id="id27">hide_muc_server</a></li>
-<li><a class="reference internal" href="#prebind" id="id28">prebind</a></li>
-<li><a class="reference internal" href="#show-controlbox-by-default" id="id29">show_controlbox_by_default</a></li>
-<li><a class="reference internal" href="#xhr-user-search" id="id30">xhr_user_search</a></li>
+<li><a class="reference internal" href="#debug" id="id26">debug</a></li>
+<li><a class="reference internal" href="#fullname" id="id27">fullname</a></li>
+<li><a class="reference internal" href="#hide-muc-server" id="id28">hide_muc_server</a></li>
+<li><a class="reference internal" href="#i18n" id="id29">i18n</a></li>
+<li><a class="reference internal" href="#prebind" id="id30">prebind</a></li>
+<li><a class="reference internal" href="#show-controlbox-by-default" id="id31">show_controlbox_by_default</a></li>
+<li><a class="reference internal" href="#xhr-user-search" id="id32">xhr_user_search</a></li>
 </ul>
 </ul>
 </li>
 </li>
 </ul>
 </ul>
 </li>
 </li>
-<li><a class="reference internal" href="#minification" id="id31">Minification</a><ul>
-<li><a class="reference internal" href="#minifying-javascript" id="id32">Minifying Javascript</a></li>
-<li><a class="reference internal" href="#minifying-css" id="id33">Minifying CSS</a></li>
+<li><a class="reference internal" href="#minification" id="id33">Minification</a><ul>
+<li><a class="reference internal" href="#minifying-javascript-and-css" id="id34">Minifying Javascript and CSS</a></li>
 </ul>
 </ul>
 </li>
 </li>
-<li><a class="reference internal" href="#translations" id="id34">Translations</a></li>
+<li><a class="reference internal" href="#translations" id="id35">Translations</a></li>
 </ul>
 </ul>
 </div>
 </div>
 <div class="section" id="quickstart-to-get-a-demo-up-and-running">
 <div class="section" id="quickstart-to-get-a-demo-up-and-running">
@@ -244,34 +245,31 @@ but this will require custom code on your server.</p>
 <div class="admonition note">
 <div class="admonition note">
 <p class="first admonition-title">Note</p>
 <p class="first admonition-title">Note</p>
 <p class="last">If you want to enable single session support, make sure to pass <strong>prebind: true</strong>
 <p class="last">If you want to enable single session support, make sure to pass <strong>prebind: true</strong>
-when you call <strong>converse.initialize</strong> (see ./index.html).</p>
+when you call <strong>converse.initialize</strong> (see ./index.html).
+Additionally you need to pass in valid <strong>jid</strong>, <strong>sid</strong>, <strong>rid</strong> and
+<strong>bosh_service_url</strong> values.</p>
 </div>
 </div>
 <p>When you authenticate to the XMPP server on your backend, you&#8217;ll receive two
 <p>When you authenticate to the XMPP server on your backend, you&#8217;ll receive two
 tokens, RID (request ID) and SID (session ID).</p>
 tokens, RID (request ID) and SID (session ID).</p>
 <p>These tokens then need to be passed back to the javascript running in your
 <p>These tokens then need to be passed back to the javascript running in your
-browser, where you will need them attach to the existing session.</p>
+browser, where you will need them to attach to the existing session.</p>
 <p>You can embed the RID and SID tokens in your HTML markup or you can do an
 <p>You can embed the RID and SID tokens in your HTML markup or you can do an
-XMLHttpRequest call to you server and ask it to return them for you.</p>
+XMLHttpRequest call to your server and ask it to return them for you.</p>
 <p>Below is one example of how this could work. An Ajax call is made to the
 <p>Below is one example of how this could work. An Ajax call is made to the
 relative URL <strong>/prebind</strong> and it expects to receive JSON data back.</p>
 relative URL <strong>/prebind</strong> and it expects to receive JSON data back.</p>
 <div class="highlight-python"><pre>$.getJSON('/prebind', function (data) {
 <div class="highlight-python"><pre>$.getJSON('/prebind', function (data) {
-        var connection = new Strophe.Connection(converse.bosh_service_url);
-        connection.attach(data.jid, data.sid, data.rid, function (status) {
-            if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED)) {
-                converse.onConnected(connection)
-            }
-        });
-    }
+    converse.initialize({
+        prebind: true,
+        bosh_service_url: data.bosh_service_url,
+        jid: data.jid,
+        sid: data.sid,
+        rid: data.rid
+    });
 );</pre>
 );</pre>
 </div>
 </div>
 <p><strong>Here&#8217;s what&#8217;s happening:</strong></p>
 <p><strong>Here&#8217;s what&#8217;s happening:</strong></p>
-<p>The JSON data contains the user&#8217;s JID (jabber ID), RID and SID. The URL to the
-BOSH connection manager is already set as a configuration setting on the
-<em>converse</em> object (see ./main.js), so we can reuse it from there.</p>
-<p>A new Strophe.Connection object is instantiated and then <em>attach</em> is called with
-the user&#8217;s JID, the necessary tokens and a callback function.</p>
-<p>In the callback function, you call <em>converse.onConnected</em> together with the
-connection object.</p>
+<p>The JSON data contains the user&#8217;s JID (jabber ID), RID, SID and the URL to the
+BOSH connection manager.</p>
 </div>
 </div>
 </div>
 </div>
 <div class="section" id="facebook-integration">
 <div class="section" id="facebook-integration">
@@ -444,42 +442,43 @@ option will create lots of extra connection traffic.</p>
 a middle man between HTTP and XMPP.</p>
 a middle man between HTTP and XMPP.</p>
 <p>See <a class="reference external" href="http://metajack.im/2008/09/08/which-bosh-server-do-you-need">here</a> for more information.</p>
 <p>See <a class="reference external" href="http://metajack.im/2008/09/08/which-bosh-server-do-you-need">here</a> for more information.</p>
 </div>
 </div>
+<div class="section" id="debug">
+<h3><a class="toc-backref" href="#id26">debug</a><a class="headerlink" href="#debug" title="Permalink to this headline">¶</a></h3>
+<p>If set to true, debugging output will be logged to the browser console.</p>
+</div>
 <div class="section" id="fullname">
 <div class="section" id="fullname">
-<h3><a class="toc-backref" href="#id26">fullname</a><a class="headerlink" href="#fullname" title="Permalink to this headline">¶</a></h3>
-<p>If you are using prebinding, you need to specify the fullname of the currently
-logged in user.</p>
+<h3><a class="toc-backref" href="#id27">fullname</a><a class="headerlink" href="#fullname" title="Permalink to this headline">¶</a></h3>
+<p>If you are using prebinding, can specify the fullname of the currently
+logged in user, otherwise the user&#8217;s vCard will be fetched.</p>
 </div>
 </div>
 <div class="section" id="hide-muc-server">
 <div class="section" id="hide-muc-server">
-<h3><a class="toc-backref" href="#id27">hide_muc_server</a><a class="headerlink" href="#hide-muc-server" title="Permalink to this headline">¶</a></h3>
-<p>Default = False</p>
+<h3><a class="toc-backref" href="#id28">hide_muc_server</a><a class="headerlink" href="#hide-muc-server" title="Permalink to this headline">¶</a></h3>
+<p>Default = false</p>
 <p>Hide the <tt class="docutils literal"><span class="pre">server</span></tt> input field of the form inside the <tt class="docutils literal"><span class="pre">Room</span></tt> panel of the
 <p>Hide the <tt class="docutils literal"><span class="pre">server</span></tt> input field of the form inside the <tt class="docutils literal"><span class="pre">Room</span></tt> panel of the
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 your choosing.</p>
 your choosing.</p>
 </div>
 </div>
+<div class="section" id="i18n">
+<h3><a class="toc-backref" href="#id29">i18n</a><a class="headerlink" href="#i18n" title="Permalink to this headline">¶</a></h3>
+<p>Specify the locale/language. The language must be in the <tt class="docutils literal"><span class="pre">locales</span></tt> object. Refer to
+<tt class="docutils literal"><span class="pre">./locale/locales.js</span></tt> to see which locales are supported.</p>
+</div>
 <div class="section" id="prebind">
 <div class="section" id="prebind">
-<h3><a class="toc-backref" href="#id28">prebind</a><a class="headerlink" href="#prebind" title="Permalink to this headline">¶</a></h3>
-<p>Default = False</p>
+<h3><a class="toc-backref" href="#id30">prebind</a><a class="headerlink" href="#prebind" title="Permalink to this headline">¶</a></h3>
+<p>Default = false</p>
 <p>Use this option when you want to attach to an existing XMPP connection that was
 <p>Use this option when you want to attach to an existing XMPP connection that was
 already authenticated (usually on the backend before page load).</p>
 already authenticated (usually on the backend before page load).</p>
 <p>This is useful when you don&#8217;t want to render the login form on the chat control
 <p>This is useful when you don&#8217;t want to render the login form on the chat control
 box with each page load.</p>
 box with each page load.</p>
-<p>When set to true, you&#8217;ll need to make sure that the onConnected method is
-called, and passed to it a Strophe connection object.</p>
-<p>Besides requiring the back-end to authenticate you, you&#8217;ll also
-have to write a Javascript snippet to attach to the set up connection:</p>
-<div class="highlight-python"><pre>$.JSON({
-    'url': 'mysite.com/xmpp-authenticate',
-    'success': function (data) {
-        connection = new Strophe.Connection(bosh_service_url);
-        connection.attach(data.jid, data.sid, data.rid, converse.onConnected);
-    }</pre>
-</div>
-<p>The backend must authenticate for you, and then return a SID (session ID) and
-RID (Request ID), which you use when you attach to the connection.</p>
+<p>For prebinding to work, your backend server must authenticate for you, and
+then return a JID (jabber ID), SID (session ID) and RID (Request ID).</p>
+<p>If you set <tt class="docutils literal"><span class="pre">prebind</span></tt> to <tt class="docutils literal"><span class="pre">true</span></tt>, you have to make sure to also pass in these
+values as <tt class="docutils literal"><span class="pre">jid</span></tt>, <tt class="docutils literal"><span class="pre">sid</span></tt>, <tt class="docutils literal"><span class="pre">rid</span></tt>.</p>
+<p>Additionally, you have to specify <tt class="docutils literal"><span class="pre">bosh_service_url</span></tt>.</p>
 </div>
 </div>
 <div class="section" id="show-controlbox-by-default">
 <div class="section" id="show-controlbox-by-default">
-<h3><a class="toc-backref" href="#id29">show_controlbox_by_default</a><a class="headerlink" href="#show-controlbox-by-default" title="Permalink to this headline">¶</a></h3>
-<p>Default = False</p>
+<h3><a class="toc-backref" href="#id31">show_controlbox_by_default</a><a class="headerlink" href="#show-controlbox-by-default" title="Permalink to this headline">¶</a></h3>
+<p>Default = false</p>
 <p>The &#8220;controlbox&#8221; refers to the special chatbox containing your contacts roster,
 <p>The &#8220;controlbox&#8221; refers to the special chatbox containing your contacts roster,
 status widget, chatrooms and other controls.</p>
 status widget, chatrooms and other controls.</p>
 <p>By default this box is hidden and can be toggled by clicking on any element in
 <p>By default this box is hidden and can be toggled by clicking on any element in
@@ -488,7 +487,7 @@ the page with class <em>toggle-online-users</em>.</p>
 page load.</p>
 page load.</p>
 </div>
 </div>
 <div class="section" id="xhr-user-search">
 <div class="section" id="xhr-user-search">
-<h3><a class="toc-backref" href="#id30">xhr_user_search</a><a class="headerlink" href="#xhr-user-search" title="Permalink to this headline">¶</a></h3>
+<h3><a class="toc-backref" href="#id32">xhr_user_search</a><a class="headerlink" href="#xhr-user-search" title="Permalink to this headline">¶</a></h3>
 <p>Default = False</p>
 <p>Default = False</p>
 <p>There are two ways to add users.</p>
 <p>There are two ways to add users.</p>
 <ul class="simple">
 <ul class="simple">
@@ -501,32 +500,25 @@ be used.</p>
 </div>
 </div>
 </div>
 </div>
 <div class="section" id="minification">
 <div class="section" id="minification">
-<h1><a class="toc-backref" href="#id31">Minification</a><a class="headerlink" href="#minification" title="Permalink to this headline">¶</a></h1>
-<div class="section" id="minifying-javascript">
-<h2><a class="toc-backref" href="#id32">Minifying Javascript</a><a class="headerlink" href="#minifying-javascript" title="Permalink to this headline">¶</a></h2>
+<h1><a class="toc-backref" href="#id33">Minification</a><a class="headerlink" href="#minification" title="Permalink to this headline">¶</a></h1>
+<div class="section" id="minifying-javascript-and-css">
+<h2><a class="toc-backref" href="#id34">Minifying Javascript and CSS</a><a class="headerlink" href="#minifying-javascript-and-css" title="Permalink to this headline">¶</a></h2>
+<p>Please make sure to read the section <a class="reference internal" href="#development">Development</a> and that you have installed
+all development dependencies (long story short, you can run <tt class="docutils literal"><span class="pre">npm</span> <span class="pre">install</span></tt>
+and then <tt class="docutils literal"><span class="pre">grunt</span> <span class="pre">fetch</span></tt>).</p>
 <p>We  use <a class="reference external" href="http://requirejs.org">require.js</a> to keep track of <em>Converse.js</em> and its dependencies and to
 <p>We  use <a class="reference external" href="http://requirejs.org">require.js</a> to keep track of <em>Converse.js</em> and its dependencies and to
 to bundle them together in a single minified file fit for deployment to a
 to bundle them together in a single minified file fit for deployment to a
 production site.</p>
 production site.</p>
-<p>To use the require.js&#8217;s optimization tool, you&#8217;ll need Node and it&#8217;s package
-manager, NPM.</p>
-<p>You can then install install require.js for Node like so:</p>
-<div class="highlight-python"><pre>npm install requirejs</pre>
-</div>
-<p>The minified javascript file is then created like this:</p>
-<div class="highlight-python"><pre>r.js -o build.js</pre>
-</div>
-<p>You should now have a new minified file (the name which is specified in build.js).</p>
-<p>You can <a class="reference external" href="http://requirejs.org/docs/optimization.html">read more about require.js&#8217;s optimizer here</a>.</p>
-</div>
-<div class="section" id="minifying-css">
-<h2><a class="toc-backref" href="#id33">Minifying CSS</a><a class="headerlink" href="#minifying-css" title="Permalink to this headline">¶</a></h2>
-<p>CSS can be minimized with Yahoo&#8217;s yuicompressor tool:</p>
-<div class="highlight-python"><pre>yui-compressor --type=css converse.css -o converse.min.css</pre>
+<p>To minify the Javascript and CSS, run the following command:</p>
+<div class="highlight-python"><pre>grunt minify</pre>
 </div>
 </div>
+<p>Javascript will be bundled and minified via <a class="reference external" href="http://requirejs.org">require.js</a>&#8216;s optimization tool.
+You can <a class="reference external" href="http://requirejs.org/docs/optimization.html">read more about require.js&#8217;s optimizer here</a>.</p>
+<p>CSS is minified via <a class="reference external" href="https://github.com/gruntjs/grunt-contrib-cssmin">cssmin</a>.</p>
 </div>
 </div>
 </div>
 </div>
 <div class="section" id="translations">
 <div class="section" id="translations">
-<h1><a class="toc-backref" href="#id34">Translations</a><a class="headerlink" href="#translations" title="Permalink to this headline">¶</a></h1>
+<h1><a class="toc-backref" href="#id35">Translations</a><a class="headerlink" href="#translations" title="Permalink to this headline">¶</a></h1>
 <div class="admonition note">
 <div class="admonition note">
 <p class="first admonition-title">Note</p>
 <p class="first admonition-title">Note</p>
 <p class="last">Translations take up a lot of space and will bloat your minified file.
 <p class="last">Translations take up a lot of space and will bloat your minified file.
@@ -612,7 +604,7 @@ those hoops you had to jump through.</p>
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="genindex.html" title="General Index"
           <a href="genindex.html" title="General Index"
              >index</a></li>
              >index</a></li>
-        <li><a href="#">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="#">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 </div>
 </div>

+ 1 - 1
docs/html/objects.inv

@@ -1,6 +1,6 @@
 # Sphinx inventory version 2
 # Sphinx inventory version 2
 # Project: Converse.js
 # Project: Converse.js
-# Version: 0.5.1
+# Version: 0.5.3
 # The remainder of this file is compressed using zlib.
 # The remainder of this file is compressed using zlib.
 xÚmÎÁ
 xÚmÎÁ
 à à{Ÿ"°³ƒ]÷;
 à à{Ÿ"°³ƒ]÷;

+ 5 - 5
docs/html/search.html

@@ -7,7 +7,7 @@
   <head>
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     
     
-    <title>Search &mdash; Converse.js 0.5.1 documentation</title>
+    <title>Search &mdash; Converse.js 0.5.3 documentation</title>
     
     
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/stylesheet.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
     <link rel="stylesheet" href="_static/pygments.css" type="text/css" />
@@ -15,7 +15,7 @@
     <script type="text/javascript">
     <script type="text/javascript">
       var DOCUMENTATION_OPTIONS = {
       var DOCUMENTATION_OPTIONS = {
         URL_ROOT:    '',
         URL_ROOT:    '',
-        VERSION:     '0.5.1',
+        VERSION:     '0.5.3',
         COLLAPSE_INDEX: false,
         COLLAPSE_INDEX: false,
         FILE_SUFFIX: '.html',
         FILE_SUFFIX: '.html',
         HAS_SOURCE:  true
         HAS_SOURCE:  true
@@ -25,7 +25,7 @@
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/underscore.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
     <script type="text/javascript" src="_static/doctools.js"></script>
     <script type="text/javascript" src="_static/searchtools.js"></script>
     <script type="text/javascript" src="_static/searchtools.js"></script>
-    <link rel="top" title="Converse.js 0.5.1 documentation" href="index.html" />
+    <link rel="top" title="Converse.js 0.5.3 documentation" href="index.html" />
   <script type="text/javascript">
   <script type="text/javascript">
     jQuery(function() { Search.loadIndex("searchindex.js"); });
     jQuery(function() { Search.loadIndex("searchindex.js"); });
   </script>
   </script>
@@ -55,7 +55,7 @@
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="genindex.html" title="General Index"
           <a href="genindex.html" title="General Index"
              accesskey="I">index</a></li>
              accesskey="I">index</a></li>
-        <li><a href="index.html">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="index.html">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 <section id="main_content" class="inner">  
 <section id="main_content" class="inner">  
@@ -100,7 +100,7 @@
         <li class="right" style="margin-right: 10px">
         <li class="right" style="margin-right: 10px">
           <a href="genindex.html" title="General Index"
           <a href="genindex.html" title="General Index"
              >index</a></li>
              >index</a></li>
-        <li><a href="index.html">Converse.js 0.5.1 documentation</a> &raquo;</li> 
+        <li><a href="index.html">Converse.js 0.5.3 documentation</a> &raquo;</li> 
       </ul>
       </ul>
     </div>
     </div>
 </div>
 </div>

File diff suppressed because it is too large
+ 0 - 0
docs/html/searchindex.js


+ 2 - 2
docs/source/conf.py

@@ -48,9 +48,9 @@ copyright = u'2013, JC Brand'
 # built documents.
 # built documents.
 #
 #
 # The short X.Y version.
 # The short X.Y version.
-version = '0.5.1'
+version = '0.6.0'
 # The full version, including alpha/beta/rc tags.
 # The full version, including alpha/beta/rc tags.
-release = '0.5.1'
+release = '0.6.0'
 
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 # for a list of supported languages.

+ 44 - 58
docs/source/index.rst

@@ -188,15 +188,17 @@ Jack Moffitt has a great `blogpost`_ about this and even provides an `example Dj
 .. Note::
 .. Note::
    If you want to enable single session support, make sure to pass **prebind: true**
    If you want to enable single session support, make sure to pass **prebind: true**
    when you call **converse.initialize** (see ./index.html).
    when you call **converse.initialize** (see ./index.html).
+   Additionally you need to pass in valid **jid**, **sid**, **rid** and
+   **bosh_service_url** values.
 
 
 When you authenticate to the XMPP server on your backend, you'll receive two
 When you authenticate to the XMPP server on your backend, you'll receive two
 tokens, RID (request ID) and SID (session ID).
 tokens, RID (request ID) and SID (session ID).
 
 
 These tokens then need to be passed back to the javascript running in your
 These tokens then need to be passed back to the javascript running in your
-browser, where you will need them attach to the existing session.
+browser, where you will need them to attach to the existing session.
 
 
 You can embed the RID and SID tokens in your HTML markup or you can do an
 You can embed the RID and SID tokens in your HTML markup or you can do an
-XMLHttpRequest call to you server and ask it to return them for you.
+XMLHttpRequest call to your server and ask it to return them for you.
 
 
 Below is one example of how this could work. An Ajax call is made to the
 Below is one example of how this could work. An Ajax call is made to the
 relative URL **/prebind** and it expects to receive JSON data back.
 relative URL **/prebind** and it expects to receive JSON data back.
@@ -204,26 +206,19 @@ relative URL **/prebind** and it expects to receive JSON data back.
 :: 
 :: 
 
 
     $.getJSON('/prebind', function (data) {
     $.getJSON('/prebind', function (data) {
-            var connection = new Strophe.Connection(converse.bosh_service_url);
-            connection.attach(data.jid, data.sid, data.rid, function (status) {
-                if ((status === Strophe.Status.ATTACHED) || (status === Strophe.Status.CONNECTED)) {
-                    converse.onConnected(connection)
-                } 
-            });
-        }
+        converse.initialize({
+            prebind: true,
+            bosh_service_url: data.bosh_service_url,
+            jid: data.jid,
+            sid: data.sid,
+            rid: data.rid
+        });
     );
     );
 
 
 **Here's what's happening:**
 **Here's what's happening:**
 
 
-The JSON data contains the user's JID (jabber ID), RID and SID. The URL to the
-BOSH connection manager is already set as a configuration setting on the
-*converse* object (see ./main.js), so we can reuse it from there.
-
-A new Strophe.Connection object is instantiated and then *attach* is called with
-the user's JID, the necessary tokens and a callback function.
-
-In the callback function, you call *converse.onConnected* together with the
-connection object.
+The JSON data contains the user's JID (jabber ID), RID, SID and the URL to the
+BOSH connection manager. 
 
 
 
 
 Facebook integration
 Facebook integration
@@ -463,25 +458,36 @@ a middle man between HTTP and XMPP.
 
 
 See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
 See `here <http://metajack.im/2008/09/08/which-bosh-server-do-you-need>`_ for more information.
 
 
+debug
+-----
+
+If set to true, debugging output will be logged to the browser console.
+
 fullname
 fullname
 --------
 --------
 
 
-If you are using prebinding, you need to specify the fullname of the currently
-logged in user.
+If you are using prebinding, can specify the fullname of the currently
+logged in user, otherwise the user's vCard will be fetched.
 
 
 hide_muc_server
 hide_muc_server
 ---------------
 ---------------
 
 
-Default = False
+Default = false
 
 
 Hide the ``server`` input field of the form inside the ``Room`` panel of the
 Hide the ``server`` input field of the form inside the ``Room`` panel of the
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 controlbox. Useful if you want to restrict users to a specific XMPP server of
 your choosing.
 your choosing.
 
 
+i18n
+----
+
+Specify the locale/language. The language must be in the ``locales`` object. Refer to
+``./locale/locales.js`` to see which locales are supported.
+
 prebind
 prebind
 --------
 --------
 
 
-Default = False
+Default = false
 
 
 Use this option when you want to attach to an existing XMPP connection that was
 Use this option when you want to attach to an existing XMPP connection that was
 already authenticated (usually on the backend before page load).
 already authenticated (usually on the backend before page load).
@@ -489,26 +495,19 @@ already authenticated (usually on the backend before page load).
 This is useful when you don't want to render the login form on the chat control
 This is useful when you don't want to render the login form on the chat control
 box with each page load.
 box with each page load.
 
 
-When set to true, you'll need to make sure that the onConnected method is 
-called, and passed to it a Strophe connection object.
+For prebinding to work, your backend server must authenticate for you, and 
+then return a JID (jabber ID), SID (session ID) and RID (Request ID).
 
 
-Besides requiring the back-end to authenticate you, you'll also 
-have to write a Javascript snippet to attach to the set up connection::
+If you set ``prebind`` to ``true``, you have to make sure to also pass in these
+values as ``jid``, ``sid``, ``rid``.
 
 
-    $.JSON({
-        'url': 'mysite.com/xmpp-authenticate',
-        'success': function (data) {
-            connection = new Strophe.Connection(bosh_service_url);
-            connection.attach(data.jid, data.sid, data.rid, converse.onConnected);
-        }
+Additionally, you have to specify ``bosh_service_url``.
 
 
-The backend must authenticate for you, and then return a SID (session ID) and
-RID (Request ID), which you use when you attach to the connection.
 
 
 show_controlbox_by_default
 show_controlbox_by_default
 --------------------------
 --------------------------
 
 
-Default = False
+Default = false
 
 
 The "controlbox" refers to the special chatbox containing your contacts roster,
 The "controlbox" refers to the special chatbox containing your contacts roster,
 status widget, chatrooms and other controls.
 status widget, chatrooms and other controls.
@@ -537,40 +536,27 @@ be used.
 Minification
 Minification
 ============
 ============
 
 
-Minifying Javascript
-====================
+Minifying Javascript and CSS
+============================
+
+Please make sure to read the section `Development`_ and that you have installed
+all development dependencies (long story short, you can run ``npm install``
+and then ``grunt fetch``).
 
 
 We  use `require.js`_ to keep track of *Converse.js* and its dependencies and to
 We  use `require.js`_ to keep track of *Converse.js* and its dependencies and to
 to bundle them together in a single minified file fit for deployment to a 
 to bundle them together in a single minified file fit for deployment to a 
 production site.
 production site.
 
 
-To use the require.js's optimization tool, you'll need Node and it's package
-manager, NPM.
-
-You can then install install require.js for Node like so:
-
-::
-
-    npm install requirejs
-
-The minified javascript file is then created like this:
+To minify the Javascript and CSS, run the following command:
 
 
 ::
 ::
 
 
-    r.js -o build.js
-
-You should now have a new minified file (the name which is specified in build.js).
+    grunt minify
 
 
+Javascript will be bundled and minified via `require.js`_'s optimization tool.
 You can `read more about require.js's optimizer here`_.
 You can `read more about require.js's optimizer here`_.
 
 
-Minifying CSS
-=============
-
-CSS can be minimized with Yahoo's yuicompressor tool:
-
-::
-
-    yui-compressor --type=css converse.css -o converse.min.css
+CSS is minified via `cssmin <https://github.com/gruntjs/grunt-contrib-cssmin>`_.
 
 
 
 
 ============
 ============

+ 7 - 0
docs/source/theme/static/stylesheet.css_t

@@ -33,3 +33,10 @@ h1 a {
 ul {
 ul {
     margin-bottom: 5px;
     margin-bottom: 5px;
 }
 }
+
+tt.literal {
+    color: #222;
+    background-color: #fff;
+    font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace;
+    font-size: 14px;
+}

+ 2 - 1
mock.js

@@ -23,7 +23,8 @@
             'unauthorize': function () {},
             'unauthorize': function () {},
             'get': function () {},
             'get': function () {},
             'subscribe': function () {},
             'subscribe': function () {},
-            'registerCallback': function () {}
+            'registerCallback': function () {},
+            'remove': function (jid, callback) { callback(); }
         },
         },
         'vcard': {
         'vcard': {
             'get': function (callback, jid) {
             'get': function (callback, jid) {

+ 31 - 49
mockup.html

@@ -192,61 +192,33 @@
     <div class="chatbox" id="37c0c87392010303765fe36b05c0967d62c6b70f" style="opacity: 1; display: inline;">
     <div class="chatbox" id="37c0c87392010303765fe36b05c0967d62c6b70f" style="opacity: 1; display: inline;">
         <div class="chat-head chat-head-chatbox">
         <div class="chat-head chat-head-chatbox">
             <a class="close-chatbox-button icon-close"></a>
             <a class="close-chatbox-button icon-close"></a>
-            <a href="" target="_blank" class="user">
-                <div class="chat-title">John Smit</div>
+            <a href="http://opkode.com" target="_blank" class="user">
+                <canvas height="35px" width="35px" class="avatar" style="background-color: black"></canvas>
+                <div class="chat-title"> JC Brand </div>
             </a>
             </a>
             <p class="user-custom-message"></p>
             <p class="user-custom-message"></p>
             <p></p>
             <p></p>
         </div>
         </div>
         <div class="chat-content">
         <div class="chat-content">
+            <div class="chat-info"><strong>/help</strong>:Show this menu</div>
+            <div class="chat-info"><strong>/me</strong>:Write in the third person</div>
             <div class="chat-message">
             <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
-            </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
+                <span class="chat-message-me">09:35 me:&nbsp;</span>
                 <span class="chat-message-content">Hello world</span>
                 <span class="chat-message-content">Hello world</span>
             </div>
             </div>
-            <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
+            <div class="chat-message ">
+                <span class="chat-message-them">19:25 Benedict-John:&nbsp;</span>
+                <span class="chat-message-content">Dagsê</span>
             </div>
             </div>
             <div class="chat-message">
             <div class="chat-message">
                 <span class="chat-message-me">19:39 me:&nbsp;</span>
                 <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
+                <span class="chat-message-content">This is a relatively long message to check that wrapping works as expected.</span>
             </div>
             </div>
             <div class="chat-message">
             <div class="chat-message">
-                <span class="chat-message-me">19:39 me:&nbsp;</span>
-                <span class="chat-message-content">Hello world</span>
+                <span class="chat-message-me">19:42 me:&nbsp;</span>
+                <span class="chat-message-content">Supercalifragilisticexpialidociousstillnotlongenough</span>
             </div>
             </div>
+            <div class="chat-event">JC Brand is busy</div>
         </div>
         </div>
         <form class="sendXMPPMessage" action="" method="post">
         <form class="sendXMPPMessage" action="" method="post">
             <ul class="chat-toolbar">
             <ul class="chat-toolbar">
@@ -263,7 +235,7 @@
         <div class="chat-head chat-head-chatroom">
         <div class="chat-head chat-head-chatroom">
             <a class="close-chatbox-button icon-close"></a>
             <a class="close-chatbox-button icon-close"></a>
             <a class="configure-chatroom-button icon-wrench" style=""></a>
             <a class="configure-chatroom-button icon-wrench" style=""></a>
-            <div class="chat-title"> converse.js </div>
+            <div class="chat-title"> Chatroom </div>
             <p class="chatroom-topic"></p>
             <p class="chatroom-topic"></p>
             <p></p>
             <p></p>
         </div>
         </div>
@@ -272,14 +244,23 @@
                 <div class="chat-content">
                 <div class="chat-content">
                     <time class="chat-date" datetime="2013-06-04T00:00:00.000Z">Tue Jun 04 2013</time>
                     <time class="chat-date" datetime="2013-06-04T00:00:00.000Z">Tue Jun 04 2013</time>
                     <div class="chat-message ">
                     <div class="chat-message ">
-                        <span class="chat-message-room">18:50 fires:&nbsp;</span>
-                        <span class="chat-message-content">explodingcoder: hi :)</span>
+                        <span class="chat-message-room">18:50 luke:&nbsp;</span>
+                        <span class="chat-message-content">leia: hi :)</span>
                     </div>
                     </div>
                     <div class="chat-message ">
                     <div class="chat-message ">
-                        <span class="chat-message-me">19:40 me:&nbsp;</span>
+                        <span class="chat-message-room">19:40 leia:&nbsp;</span>
                         <span class="chat-message-content">
                         <span class="chat-message-content">
                             I'll be gone for a while, will be back in about an hour</span>
                             I'll be gone for a while, will be back in about an hour</span>
                     </div>
                     </div>
+                    <div class="chat-message ">
+                        <span class="chat-message-room">19:40 Obi-wan Kenobi, Jedi Master:&nbsp;</span>
+                        <span class="chat-message-content">
+                            I'll be gone for a while, will be back in about an hour</span>
+                    </div>
+                    <div class="chat-message">
+                        <span class="chat-message-me">19:42 me:&nbsp;</span>
+                        <span class="chat-message-content">Supercalifragilisticexpialidociousstillnotlongenough</span>
+                    </div>
                 </div>
                 </div>
                 <form class="sendXMPPMessage" action="" method="post">
                 <form class="sendXMPPMessage" action="" method="post">
                     <textarea type="text" class="chat-textarea" placeholder="Message"></textarea>
                     <textarea type="text" class="chat-textarea" placeholder="Message"></textarea>
@@ -287,9 +268,10 @@
             </div>
             </div>
             <div class="participants">
             <div class="participants">
                 <ul class="participant-list">
                 <ul class="participant-list">
-                    <li class="participant" title="This user can send messages in this room">jabberthehut</li>
-                    <li class="participant" title="This user can send messages in this room">explodingcoder</li>
-                    <li class="moderator" title="This user is a moderator">jcbrand</li>
+                    <li class="participant" title="This user can send messages in this room">Obi-wan Kenobi, Jedi Master</li>
+                    <li class="participant" title="This user can send messages in this room">jabber the hut</li>
+                    <li class="participant" title="This user can send messages in this room">leia</li>
+                    <li class="moderator" title="This user is a moderator">luke</li>
                 </ul>
                 </ul>
             </div>
             </div>
         </div>
         </div>
@@ -299,7 +281,7 @@
         <div class="chat-head chat-head-chatroom">
         <div class="chat-head chat-head-chatroom">
             <a class="close-chatbox-button icon-close"></a>
             <a class="close-chatbox-button icon-close"></a>
             <a class="configure-chatroom-button" style="display:none">&nbsp;</a>
             <a class="configure-chatroom-button" style="display:none">&nbsp;</a>
-            <div class="chat-title"> problematic </div>
+            <div class="chat-title"> Restricted Chatroom</div>
             <p class="chatroom-topic"></p>
             <p class="chatroom-topic"></p>
             <p></p>
             <p></p>
         </div>
         </div>

+ 0 - 1
non_amd.html

@@ -16,7 +16,6 @@
     <script type="text/javascript" src="components/underscore/underscore.js"></script>
     <script type="text/javascript" src="components/underscore/underscore.js"></script>
     <script type="text/javascript" src="components/backbone//backbone.js"></script>
     <script type="text/javascript" src="components/backbone//backbone.js"></script>
     <script type="text/javascript" src="components/backbone.localStorage/backbone.localStorage.js"></script>
     <script type="text/javascript" src="components/backbone.localStorage/backbone.localStorage.js"></script>
-    <script type="text/javascript" src="components/sjcl/sjcl.js"></script>
     <script type="text/javascript" src="components/tinysort/src/jquery.tinysort.js"></script>
     <script type="text/javascript" src="components/tinysort/src/jquery.tinysort.js"></script>
     <script type="text/javascript" src="components/jed/jed.js"></script>
     <script type="text/javascript" src="components/jed/jed.js"></script>
     <script type="text/javascript" src="locale/en/LC_MESSAGES/en.js"></script>
     <script type="text/javascript" src="locale/en/LC_MESSAGES/en.js"></script>

+ 0 - 9
spec/ChatRoomSpec.js

@@ -135,7 +135,6 @@
                     .c('not-authorized').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('not-authorized').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
 
 
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'renderPasswordForm').andCallThrough();
                 spyOn(view, 'renderPasswordForm').andCallThrough();
                 runs(function () {
                 runs(function () {
                     view.onChatRoomPresence(presence, {'nick': 'dummy'});
                     view.onChatRoomPresence(presence, {'nick': 'dummy'});
@@ -159,7 +158,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
                     .c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('registration-required').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe('You are not on the member list of this room');
                 expect(view.$el.find('.chat-body p').text()).toBe('You are not on the member list of this room');
@@ -175,7 +173,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'auth'})
                     .c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('forbidden').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe('You have been banned from this room');
                 expect(view.$el.find('.chat-body p').text()).toBe('You have been banned from this room');
@@ -191,7 +188,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'modify'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'modify'})
                     .c('jid-malformed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('jid-malformed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe('No nickname was specified');
                 expect(view.$el.find('.chat-body p').text()).toBe('No nickname was specified');
@@ -207,7 +203,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                     .c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('not-allowed').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe('You are not allowed to create new rooms');
                 expect(view.$el.find('.chat-body p').text()).toBe('You are not allowed to create new rooms');
@@ -223,7 +218,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                     .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('not-acceptable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe("Your nickname doesn't conform to this room's policies");
                 expect(view.$el.find('.chat-body p').text()).toBe("Your nickname doesn't conform to this room's policies");
@@ -239,7 +233,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                     .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('conflict').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe("Your nickname is already taken");
                 expect(view.$el.find('.chat-body p').text()).toBe("Your nickname is already taken");
@@ -255,7 +248,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                     .c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('item-not-found').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe("This room does not (yet) exist");
                 expect(view.$el.find('.chat-body p').text()).toBe("This room does not (yet) exist");
@@ -271,7 +263,6 @@
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                 .c('error').attrs({by:'coven@chat.shakespeare.lit', type:'cancel'})
                     .c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                     .c('service-unavailable').attrs({xmlns:'urn:ietf:params:xml:ns:xmpp-stanzas'}).nodeTree;
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
                 var view = this.chatboxesview.views['problematic@muc.localhost'];
-                spyOn(converse.connection.muc, 'removeRoom');
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 spyOn(view, 'showErrorMessage').andCallThrough();
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 view.onChatRoomPresence(presence, {'nick': 'dummy'});
                 expect(view.$el.find('.chat-body p').text()).toBe("This room has reached it's maximum number of occupants");
                 expect(view.$el.find('.chat-body p').text()).toBe("This room has reached it's maximum number of occupants");

+ 45 - 5
spec/MainSpec.js

@@ -12,7 +12,7 @@
             'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer'
             'Louw Spekman', 'Mohamad Stet', 'Dominik Beyer'
         ];
         ];
         var pend_names = [
         var pend_names = [
-            'Suleyman van Beusichem', 'Nicole Diederich', 'Nanja van Yperen'
+            'Suleyman van Beusichem', 'Nanja van Yperen', 'Nicole Diederich'
         ];
         ];
         var cur_names = [
         var cur_names = [
             'Max Frankfurter', 'Candice van der Knijff', 'Irini Vlastuin', 'Rinse Sommer', 'Annegreet Gomez',
             'Max Frankfurter', 'Candice van der Knijff', 'Irini Vlastuin', 'Rinse Sommer', 'Annegreet Gomez',
@@ -123,6 +123,48 @@
                     expect(this.rosterview.$el.find('dt#pending-xmpp-contacts').css('display')).toEqual('none');
                     expect(this.rosterview.$el.find('dt#pending-xmpp-contacts').css('display')).toEqual('none');
                 }, converse));
                 }, converse));
 
 
+                it("can be added to the roster", $.proxy(function () {
+                    spyOn(this.rosterview, 'render').andCallThrough();
+                    spyOn(this.xmppstatus, 'sendPresence');
+                    this.roster.create({
+                        jid: pend_names[0].replace(' ','.').toLowerCase() + '@localhost',
+                        subscription: 'none',
+                        ask: 'subscribe',
+                        fullname: pend_names[0],
+                        is_last: true 
+                    });
+                    expect(this.rosterview.$el.is(':visible')).toEqual(true);
+                    expect(this.xmppstatus.sendPresence).toHaveBeenCalled();
+                    expect(this.rosterview.render).toHaveBeenCalled();
+                }, converse));
+
+                it("can be removed by the user", $.proxy(function () {
+                    var view = _.toArray(this.rosterview.rosteritemviews).pop();
+                    spyOn(window, 'confirm').andReturn(true);
+                    spyOn(this.connection.roster, 'remove').andCallThrough();
+                    spyOn(this.connection.roster, 'unauthorize');
+                    spyOn(this.rosterview.model, 'remove').andCallThrough();
+                    //spyOn(view, 'removeContact').andCallThrough();
+
+                    runs($.proxy(function () {
+                        view.$el.find('.remove-xmpp-contact').click();
+                    }, converse));
+                    waits(500);
+                    runs($.proxy(function () {
+                        expect(window.confirm).toHaveBeenCalled();
+                        //expect(view.removeContact).toHaveBeenCalled();
+                        expect(this.connection.roster.remove).toHaveBeenCalled();
+                        expect(this.connection.roster.unauthorize).toHaveBeenCalled();
+                        expect(this.rosterview.model.remove).toHaveBeenCalled();
+                        // The element must now be detached from the DOM.
+                        expect(view.$el.closest('html').length).toBeFalsy();
+                    }, converse));
+                }, converse));
+
+                it("will lose their own heading once the last one has been removed", $.proxy(function () {
+                    expect(this.rosterview.$el.find('dt#pending-xmpp-contacts').is(':visible')).toBeFalsy();
+                }, converse));
+
                 it("can be added to the roster and they will be sorted alphabetically", $.proxy(function () {
                 it("can be added to the roster and they will be sorted alphabetically", $.proxy(function () {
                     var i, t, is_last;
                     var i, t, is_last;
                     spyOn(this.rosterview, 'render').andCallThrough();
                     spyOn(this.rosterview, 'render').andCallThrough();
@@ -136,13 +178,10 @@
                             fullname: pend_names[i],
                             fullname: pend_names[i],
                             is_last: is_last
                             is_last: is_last
                         });
                         });
-                        // For performance reasons, the roster should only be shown once
-                        // the last contact has been added.
                         if (is_last) {
                         if (is_last) {
-                            expect(this.rosterview.$el.is(':visible')).toEqual(true);
                             expect(this.xmppstatus.sendPresence).toHaveBeenCalled();
                             expect(this.xmppstatus.sendPresence).toHaveBeenCalled();
                         } else {
                         } else {
-                            expect(this.rosterview.$el.is(':visible')).toEqual(false);
+                            expect(this.xmppstatus.sendPresence).not.toHaveBeenCalled();
                         }
                         }
                         expect(this.rosterview.render).toHaveBeenCalled();
                         expect(this.rosterview.render).toHaveBeenCalled();
                         // Check that they are sorted alphabetically
                         // Check that they are sorted alphabetically
@@ -154,6 +193,7 @@
                 it("will have their own heading once they have been added", $.proxy(function () {
                 it("will have their own heading once they have been added", $.proxy(function () {
                     expect(this.rosterview.$el.find('dt#pending-xmpp-contacts').css('display')).toEqual('block');
                     expect(this.rosterview.$el.find('dt#pending-xmpp-contacts').css('display')).toEqual('block');
                 }, converse));
                 }, converse));
+
             }, converse));
             }, converse));
 
 
             describe("Existing Contacts", $.proxy(function () {
             describe("Existing Contacts", $.proxy(function () {

+ 29 - 31
tests_main.js

@@ -68,37 +68,35 @@ require([
             prebind: false,
             prebind: false,
             xhr_user_search: false,
             xhr_user_search: false,
             auto_subscribe: false,
             auto_subscribe: false,
-            animate: false
+            animate: false,
+            connection: mock_connection,
+            testing: true
+        }, function (converse) {
+            window.converse = converse;
+            require([
+                "jasmine-console-reporter",
+                "jasmine-junit-reporter",
+                "spec/MainSpec",
+                "spec/ChatRoomSpec"
+            ], function () {
+                // Jasmine stuff
+                var jasmineEnv = jasmine.getEnv();
+                if (/PhantomJS/.test(navigator.userAgent)) {
+                    jasmineEnv.addReporter(new jasmine.TrivialReporter());
+                    jasmineEnv.addReporter(new jasmine.JUnitXmlReporter('./test-reports/'));
+                    jasmineEnv.addReporter(new jasmine.ConsoleReporter());
+                    jasmineEnv.updateInterval = 0;
+                } else {
+                    var htmlReporter = new jasmine.HtmlReporter();
+                    jasmineEnv.addReporter(htmlReporter);
+                    jasmineEnv.addReporter(new jasmine.ConsoleReporter());
+                    jasmineEnv.specFilter = function(spec) {
+                        return htmlReporter.specFilter(spec);
+                    };
+                    jasmineEnv.updateInterval = 200;
+                }
+                jasmineEnv.execute();
+            });
         });
         });
-        converse.onConnected(
-            mock_connection, 
-            function (converse) {
-                window.converse = converse;
-                require([
-                    "jasmine-console-reporter",
-                    "jasmine-junit-reporter",
-                    "spec/MainSpec",
-                    "spec/ChatRoomSpec"
-                ], function () {
-                    // Jasmine stuff
-                    var jasmineEnv = jasmine.getEnv();
-                    if (/PhantomJS/.test(navigator.userAgent)) {
-                        jasmineEnv.addReporter(new jasmine.TrivialReporter());
-                        jasmineEnv.addReporter(new jasmine.JUnitXmlReporter('./test-reports/'));
-                        jasmineEnv.addReporter(new jasmine.ConsoleReporter());
-                        jasmineEnv.updateInterval = 0;
-                    } else {
-                        var htmlReporter = new jasmine.HtmlReporter();
-                        jasmineEnv.addReporter(htmlReporter);
-                        jasmineEnv.addReporter(new jasmine.ConsoleReporter());
-                        jasmineEnv.specFilter = function(spec) {
-                            return htmlReporter.specFilter(spec);
-                        };
-                        jasmineEnv.updateInterval = 200;
-                    }
-                    jasmineEnv.execute();
-                });
-            }
-        );
     }
     }
 );
 );

Some files were not shown because too many files changed in this diff