Browse Source

Some work on using es6 promises

JC Brand 8 years ago
parent
commit
6ef0536e31

+ 61 - 52
spec/roomslist.js

@@ -3,6 +3,7 @@
 } (this, function (jasmine, mock, converse, roomslist, test_utils) {
     var _ = converse.env._;
     var $msg = converse.env.$msg;
+    var Promise = converse.env.Promise;
 
     describe("The converse-roomslist plugin", function () {
 
@@ -77,61 +78,69 @@
             expect(_converse.chatboxes.length).toBe(1);
         }));
 
-        it("shows unread messages directed at the user", mock.initConverse(
+        it("shows unread messages directed at the user", mock.initConverseWithAsync(
             { whitelisted_plugins: ['converse-roomslist'],
               allow_bookmarks: false // Makes testing easier, otherwise we
                                      // have to mock stanza traffic.
-            }, function (_converse) {
-
-            var room_jid = 'kitchen@conference.shakespeare.lit';
-            test_utils.openAndEnterChatRoom(
-                _converse, 'kitchen', 'conference.shakespeare.lit', 'romeo');
-            var view = _converse.chatboxviews.get(room_jid);
-            view.model.set({'minimized': true});
-
-            var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
-            var nick = mock.chatroom_names[0];
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('foo').tree());
-
-            // If the user isn't mentioned, the counter doesn't get incremented, but the text of the room is bold
-            var room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
-            expect(_.includes(room_el.classList, 'unread-msgs'));
-
-            // If the user is mentioned, the counter also gets updated
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('romeo: Your attention is required').tree()
-            );
-            var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(indicator_el.textContent).toBe('1');
-
-            view.handleMUCMessage(
-                $msg({
-                    from: room_jid+'/'+nick,
-                    id: (new Date()).getTime(),
-                    to: 'dummy@localhost',
-                    type: 'groupchat'
-                }).c('body').t('romeo: and another thing...').tree()
-            );
-            indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(indicator_el.textContent).toBe('2');
-
-            // When the chat gets maximized again, the unread indicators are removed
-            view.model.set({'minimized': false});
-            indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
-            expect(_.isNull(indicator_el));
-            room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
-            expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
+            }, function (done, _converse) {
+
+            test_utils.waitUntil(function () {
+                    return !_.isUndefined(_converse.rooms_list_view)
+                }, 500)
+            .then(function () {
+                var room_jid = 'kitchen@conference.shakespeare.lit';
+                test_utils.openAndEnterChatRoom(
+                    _converse, 'kitchen', 'conference.shakespeare.lit', 'romeo');
+                var view = _converse.chatboxviews.get(room_jid);
+                view.model.set({'minimized': true});
+
+                var contact_jid = mock.cur_names[5].replace(/ /g,'.').toLowerCase() + '@localhost';
+                var nick = mock.chatroom_names[0];
+                view.handleMUCMessage(
+                    $msg({
+                        from: room_jid+'/'+nick,
+                        id: (new Date()).getTime(),
+                        to: 'dummy@localhost',
+                        type: 'groupchat'
+                    }).c('body').t('foo').tree());
+
+                // If the user isn't mentioned, the counter doesn't get incremented, but the text of the room is bold
+                var room_el = _converse.rooms_list_view.el.querySelector(
+                    ".available-chatroom"
+                );
+                expect(_.includes(room_el.classList, 'unread-msgs'));
+
+                // If the user is mentioned, the counter also gets updated
+                view.handleMUCMessage(
+                    $msg({
+                        from: room_jid+'/'+nick,
+                        id: (new Date()).getTime(),
+                        to: 'dummy@localhost',
+                        type: 'groupchat'
+                    }).c('body').t('romeo: Your attention is required').tree()
+                );
+                var indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                expect(indicator_el.textContent).toBe('1');
+
+                view.handleMUCMessage(
+                    $msg({
+                        from: room_jid+'/'+nick,
+                        id: (new Date()).getTime(),
+                        to: 'dummy@localhost',
+                        type: 'groupchat'
+                    }).c('body').t('romeo: and another thing...').tree()
+                );
+                indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                expect(indicator_el.textContent).toBe('2');
+
+                // When the chat gets maximized again, the unread indicators are removed
+                view.model.set({'minimized': false});
+                indicator_el = _converse.rooms_list_view.el.querySelector(".msgs-indicator");
+                expect(_.isNull(indicator_el));
+                room_el = _converse.rooms_list_view.el.querySelector(".available-chatroom");
+                expect(_.includes(room_el.classList, 'unread-msgs')).toBeFalsy();
+                done();
+            });
         }));
     });
 }));

+ 1 - 1
src/config.js

@@ -23,7 +23,7 @@ require.config({
         "backbone.browserStorage":  "node_modules/backbone.browserStorage/backbone.browserStorage",
         "backbone.overview":        "node_modules/backbone.overview/backbone.overview",
         "eventemitter":             "node_modules/otr/build/dep/eventemitter",
-        "es6-promise":              "node_modules/es6-promise/dist/es6-promise",
+        "es6-promise":              "node_modules/es6-promise/dist/es6-promise.auto",
         "jquery":                   "node_modules/jquery/dist/jquery",
         "jquery.noconflict":        "src/jquery.noconflict",
         "jquery.browser":           "node_modules/jquery.browser/dist/jquery.browser",

+ 9 - 5
src/converse-bookmarks.js

@@ -10,7 +10,8 @@
  * in XEP-0048.
  */
 (function (root, factory) {
-    define(["utils",
+    define(["jquery.noconflict",
+            "utils",
             "converse-core",
             "converse-muc",
             "tpl!chatroom_bookmark_form",
@@ -20,6 +21,7 @@
         ],
         factory);
 }(this, function (
+        $,
         utils,
         converse,
         muc,
@@ -29,8 +31,7 @@
         tpl_bookmarks_list
     ) {
 
-    const $ = converse.env.jQuery,
-          { Backbone, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
+    const { Backbone, Promise, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
 
     converse.plugins.add('converse-bookmarks', {
         overrides: {
@@ -488,8 +489,11 @@
                     _converse.emit('bookmarksInitialized');
                 });
             };
-            $.when(_converse.api.waitUntil('chatBoxesFetched'),
-                   _converse.api.waitUntil('roomsPanelRendered')).then(initBookmarks);
+
+            Promise.all([
+                _converse.api.waitUntil('chatBoxesFetched'),
+                _converse.api.waitUntil('roomsPanelRendered')
+            ]).then(initBookmarks);
 
             const afterReconnection = function () {
                 if (!_converse.allow_bookmarks) {

+ 3 - 2
src/converse-chatview.js

@@ -8,6 +8,7 @@
 
 (function (root, factory) {
     define([
+            "jquery.noconflict",
             "converse-core",
             "tpl!chatbox",
             "tpl!new_day",
@@ -19,6 +20,7 @@
             "tpl!spinner"
     ], factory);
 }(this, function (
+            $,
             converse,
             tpl_chatbox,
             tpl_new_day,
@@ -30,8 +32,7 @@
             tpl_spinner
     ) {
     "use strict";
-    const $ = converse.env.jQuery,
-          { $msg, Backbone, Strophe, _, moment, utils } = converse.env;
+    const { $msg, Backbone, Strophe, _, moment, utils } = converse.env;
 
     const KEY = {
         ENTER: 13,

+ 4 - 8
src/converse-controlbox.js

@@ -7,7 +7,8 @@
 /*global define */
 
 (function (root, factory) {
-    define(["converse-core",
+    define(["jquery.noconflict",
+            "converse-core",
             "tpl!add_contact_dropdown",
             "tpl!add_contact_form",
             "tpl!change_status_message",
@@ -25,6 +26,7 @@
             "converse-rosterview"
     ], factory);
 }(this, function (
+            $,
             converse,
             tpl_add_contact_dropdown,
             tpl_add_contact_form,
@@ -44,13 +46,7 @@
 
     const USERS_PANEL_ID = 'users';
     const CHATBOX_TYPE = 'chatbox';
-    // Strophe methods for building stanzas
-    const { Strophe } = converse.env,
-        { Backbone } = converse.env,
-        { utils } = converse.env;
-    // Other necessary globals
-    const $ = converse.env.jQuery,
-         { _, fp, moment } = converse.env;
+    const { Strophe, Backbone, utils, _, fp, moment } = converse.env;
 
 
     converse.plugins.add('converse-controlbox', {

+ 128 - 116
src/converse-core.js

@@ -7,7 +7,7 @@
 /*global Backbone, define, window, document, JSON */
 (function (root, factory) {
     define(["sizzle",
-            "jquery.noconflict",
+            "es6-promise",
             "lodash.noconflict",
             "lodash.converter",
             "polyfill",
@@ -21,7 +21,7 @@
             "backbone.overview",
     ], factory);
 }(this, function (
-        sizzle, $, _, lodashConverter, polyfill,
+        sizzle, Promise, _, lodashConverter, polyfill,
         utils, moment, Strophe, pluggable, Backbone) {
 
     /* Cannot use this due to Safari bug.
@@ -47,20 +47,29 @@
         'interpolate': /\{\{([\s\S]+?)\}\}/g
     };
 
-    const _converse = {};
-    _converse.templates = {};
+    const _converse = {
+        'templates': {},
+        'promises': {}
+    }
     _.extend(_converse, Backbone.Events);
-    _converse.promises = {
-        'cachedRoster': new $.Deferred(),
-        'chatBoxesFetched': new $.Deferred(),
-        'connected': new $.Deferred(),
-        'pluginsInitialized': new $.Deferred(),
-        'roster': new $.Deferred(),
-        'rosterContactsFetched': new $.Deferred(),
-        'rosterGroupsFetched': new $.Deferred(),
-        'rosterInitialized': new $.Deferred(),
-        'statusInitialized': new $.Deferred()
-    };
+
+    const PROMISES = [
+        'cachedRoster',
+        'chatBoxesFetched',
+        'connected',
+        'pluginsInitialized',
+        'roster',
+        'rosterContactsFetched',
+        'rosterGroupsFetched',
+        'rosterInitialized',
+        'statusInitialized'
+    ];
+
+    function addPromise (promise) {
+        _converse.promises[promise] = utils.getWrappedPromise();
+    }
+    _.each(PROMISES, addPromise);
+
     _converse.emit = function (name) {
         _converse.trigger.apply(this, arguments);
         const promise = _converse.promises[name];
@@ -167,7 +176,7 @@
     _converse.initialize = function (settings, callback) {
         "use strict";
         settings = !_.isUndefined(settings) ? settings : {};
-        const init_deferred = new $.Deferred();
+        const init_promise = utils.getWrappedPromise();
 
         if (!_.isUndefined(_converse.chatboxes)) {
             // Looks like _converse.initialized was called again without logging
@@ -291,8 +300,6 @@
             }
         }
 
-        $.fx.off = !this.animate;
-
         // Module-level variables
         // ----------------------
         this.callback = callback || _.noop;
@@ -555,19 +562,19 @@
             }
         };
 
-        this.initStatus = function () {
-            const deferred = new $.Deferred();
-            this.xmppstatus = new this.XMPPStatus();
-            const id = b64_sha1(`converse.xmppstatus-${_converse.bare_jid}`);
-            this.xmppstatus.id = id; // Appears to be necessary for backbone.browserStorage
-            this.xmppstatus.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
-            this.xmppstatus.fetch({
-                success: deferred.resolve,
-                error: deferred.resolve
+        this.initStatus = () => 
+            new Promise((resolve, reject) => {
+                const promise = new utils.getWrappedPromise();
+                this.xmppstatus = new this.XMPPStatus();
+                const id = b64_sha1(`converse.xmppstatus-${_converse.bare_jid}`);
+                this.xmppstatus.id = id; // Appears to be necessary for backbone.browserStorage
+                this.xmppstatus.browserStorage = new Backbone.BrowserStorage[_converse.storage](id);
+                this.xmppstatus.fetch({
+                    success: resolve,
+                    error: resolve
+                });
+                _converse.emit('statusInitialized');
             });
-            _converse.emit('statusInitialized');
-            return deferred.promise();
-        };
 
         this.initSession = function () {
             this.session = new this.Session();
@@ -751,7 +758,7 @@
             if (reconnecting) {
                 _converse.xmppstatus.sendPresence();
             } else {
-                init_deferred.resolve();
+                init_promise.resolve();
                 _converse.emit('initialized');
             }
         };
@@ -788,7 +795,10 @@
                 // close them now.
                 _converse.chatboxviews.closeAllChatBoxes();
                 _converse.features = new _converse.Features();
-                _converse.initStatus().done(_.partial(_converse.onStatusInitialized, false));
+                _converse.initStatus().then(
+                    _.partial(_converse.onStatusInitialized, false),
+                    _.partial(_converse.onStatusInitialized, false)
+                );
                 _converse.emit('connected');
             }
         };
@@ -1026,7 +1036,7 @@
                                 _converse.connection.flush();
                                 _converse.roster.subscribeToSuggestedItems.bind(_converse.roster)(msg);
                             }, t);
-                        t += $(msg).find('item').length*250;
+                        t += msg.querySelectorAll('item').length*250;
                         return true;
                     },
                     Strophe.NS.ROSTERX, 'message', null
@@ -1041,26 +1051,26 @@
                  * Returns a promise which resolves once the contacts have been
                  * fetched.
                  */
-                const deferred = new $.Deferred();
-                this.fetch({
-                    add: true,
-                    success (collection) {
-                        if (collection.length === 0) {
-                            /* We don't have any roster contacts stored in sessionStorage,
-                             * so lets fetch the roster from the XMPP server. We pass in
-                             * 'sendPresence' as callback method, because after initially
-                             * fetching the roster we are ready to receive presence
-                             * updates from our contacts.
-                             */
-                            _converse.send_initial_presence = true;
-                            _converse.roster.fetchFromServer(deferred.resolve);
-                        } else {
-                            _converse.emit('cachedRoster', collection);
-                            deferred.resolve();
+                return new Promise((resolve, reject) => {
+                    this.fetch({
+                        add: true,
+                        success (collection) {
+                            if (collection.length === 0) {
+                                /* We don't have any roster contacts stored in sessionStorage,
+                                * so lets fetch the roster from the XMPP server. We pass in
+                                * 'sendPresence' as callback method, because after initially
+                                * fetching the roster we are ready to receive presence
+                                * updates from our contacts.
+                                */
+                                _converse.send_initial_presence = true;
+                                _converse.roster.fetchFromServer(resolve);
+                            } else {
+                                _converse.emit('cachedRoster', collection);
+                                resolve();
+                            }
                         }
-                    }
+                    });
                 });
-                return deferred.promise();
             },
 
             subscribeToSuggestedItems (msg) {
@@ -1091,11 +1101,12 @@
                  *      reason for the subscription request.
                  *    (Object) attributes - Any additional attributes to be stored on the user's model.
                  */
-                this.addContact(jid, name, groups, attributes).done(function (contact) {
+                const handler = (contact) => {
                     if (contact instanceof _converse.RosterContact) {
                         contact.subscribe(message);
                     }
-                });
+                }
+                this.addContact(jid, name, groups, attributes).then(handler, handler);
             },
 
             sendContactAddIQ (jid, name, groups, callback, errback) {
@@ -1128,28 +1139,28 @@
                  *    (Array of Strings) groups - Any roster groups the user might belong to
                  *    (Object) attributes - Any additional attributes to be stored on the user's model.
                  */
-                const deferred = new $.Deferred();
-                groups = groups || [];
-                name = _.isEmpty(name)? jid: name;
-                this.sendContactAddIQ(jid, name, groups,
-                    () => {
-                        const contact = this.create(_.assignIn({
-                            ask: undefined,
-                            fullname: name,
-                            groups,
-                            jid,
-                            requesting: false,
-                            subscription: 'none'
-                        }, attributes), {sort: false});
-                        deferred.resolve(contact);
-                    },
-                    function (err) {
-                        alert(__(`Sorry, there was an error while trying to add ${name} as a contact.`));
-                        _converse.log(err, Strophe.LogLevel.ERROR);
-                        deferred.resolve(err);
-                    }
-                );
-                return deferred.promise();
+                return new Promise((resolve, reject) => {
+                    groups = groups || [];
+                    name = _.isEmpty(name)? jid: name;
+                    this.sendContactAddIQ(jid, name, groups,
+                        () => {
+                            const contact = this.create(_.assignIn({
+                                ask: undefined,
+                                fullname: name,
+                                groups,
+                                jid,
+                                requesting: false,
+                                subscription: 'none'
+                            }, attributes), {sort: false});
+                            resolve(contact);
+                        },
+                        function (err) {
+                            alert(__(`Sorry, there was an error while trying to add ${name} as a contact.`));
+                            _converse.log(err, Strophe.LogLevel.ERROR);
+                            resolve(err);
+                        }
+                    );
+                });
             },
 
             subscribeBack (bare_jid) {
@@ -1158,11 +1169,12 @@
                     contact.authorize().subscribe();
                 } else {
                     // Can happen when a subscription is retried or roster was deleted
-                    this.addContact(bare_jid, '', [], { 'subscription': 'from' }).done(function (contact) {
+                    const handler = (contact) => {
                         if (contact instanceof _converse.RosterContact) {
                             contact.authorize().subscribe();
                         }
-                    });
+                    }
+                    this.addContact(bare_jid, '', [], { 'subscription': 'from' }).then(handler, handler);
                 }
             },
 
@@ -1379,14 +1391,14 @@
                  * Returns a promise which resolves once the groups have been
                  * returned.
                  */
-                const deferred = new $.Deferred();
-                this.fetch({
-                    silent: true, // We need to first have all groups before
-                                  // we can start positioning them, so we set
-                                  // 'silent' to true.
-                    success: deferred.resolve
+                return new Promise((resolve, reject) => {
+                    this.fetch({
+                        silent: true, // We need to first have all groups before
+                                    // we can start positioning them, so we set
+                                    // 'silent' to true.
+                        success: resolve
+                    });
                 });
-                return deferred.promise();
             }
         });
 
@@ -1935,30 +1947,29 @@
             }
         };
 
-        this.fetchLoginCredentials = function () {
-            const deferred = new $.Deferred();
-            const xhr = new XMLHttpRequest();
-            xhr.open('GET', _converse.credentials_url, true);
-            xhr.setRequestHeader('Accept', "application/json, text/javascript");
-            xhr.onload = function() {
-                if (xhr.status >= 200 && xhr.status < 400) {
-                    const data = JSON.parse(xhr.responseText);
-                    deferred.resolve({
-                        'jid': data.jid,
-                        'password': data.password
-                    });
-                } else {
-                    xhr.onerror();
-                }
-            };
-            xhr.onerror = function () {
-                delete _converse.connection;
-                _converse.emit('noResumeableSession');
-                deferred.reject(xhr.responseText);
-            };
-            xhr.send();
-            return deferred.promise();
-        };
+        this.fetchLoginCredentials = () =>
+            new Promise((resolve, reject) => {
+                const xhr = new XMLHttpRequest();
+                xhr.open('GET', _converse.credentials_url, true);
+                xhr.setRequestHeader('Accept', "application/json, text/javascript");
+                xhr.onload = function() {
+                    if (xhr.status >= 200 && xhr.status < 400) {
+                        const data = JSON.parse(xhr.responseText);
+                        resolve({
+                            'jid': data.jid,
+                            'password': data.password
+                        });
+                    } else {
+                        xhr.onerror();
+                    }
+                };
+                xhr.onerror = function () {
+                    delete _converse.connection;
+                    _converse.emit('noResumeableSession');
+                    reject(xhr.responseText);
+                };
+                xhr.send();
+            });
 
         this.startNewBOSHSession = function () {
             const xhr = new XMLHttpRequest();
@@ -2045,7 +2056,10 @@
                     // or credentials fetching via HTTP
                     this.autoLogin(credentials);
                 } else if (this.credentials_url) {
-                    this.fetchLoginCredentials().done(this.autoLogin.bind(this));
+                    this.fetchLoginCredentials().then(
+                        this.autoLogin.bind(this),
+                        this.autoLogin.bind(this)
+                    );
                 } else if (!this.jid) {
                     throw new Error(
                         "attemptNonPreboundSession: If you use auto_login, "+
@@ -2203,7 +2217,7 @@
             _converse.connection.service === 'jasmine tests') {
             return _converse;
         } else {
-            return init_deferred.promise();
+            return init_promise.promise;
         }
     };
 
@@ -2280,9 +2294,7 @@
         'promises': {
             'add' (promises) {
                 promises = _.isArray(promises) ? promises : [promises]
-                _.each(promises, function (promise) {
-                    _converse.promises[promise] = new $.Deferred();
-                });
+                _.each(promises, addPromise);
             }
         },
         'contacts': {
@@ -2387,7 +2399,7 @@
             if (_.isUndefined(promise)) {
                 return null;
             }
-            return _converse.promises[name].promise();
+            return promise.promise;
         },
         'send' (stanza) {
             _converse.connection.send(stanza);
@@ -2417,11 +2429,11 @@
             '$msg': $msg,
             '$pres': $pres,
             'Backbone': Backbone,
+            'Promise': Promise,
             'Strophe': Strophe,
             '_': _,
-            'fp': fp,
             'b64_sha1':  b64_sha1,
-            'jQuery': $,
+            'fp': fp,
             'moment': moment,
             'sizzle': sizzle,
             'utils': utils

+ 3 - 4
src/converse-dragresize.js

@@ -7,17 +7,16 @@
 /*global define, window */
 
 (function (root, factory) {
-    define([
+    define(["jquery.noconflict",
             "converse-core",
             "tpl!dragresize",
             "converse-chatview",
             "converse-muc", // XXX: would like to remove this
             "converse-controlbox"
     ], factory);
-}(this, function (converse, tpl_dragresize) {
+}(this, function ($, converse, tpl_dragresize) {
     "use strict";
-    const $ = converse.env.jQuery,
-        { _ } = converse.env;
+    const { _ } = converse.env;
 
     function renderDragResizeHandles (_converse, view) {
         const flyout = view.el.querySelector('.box-flyout');

+ 1 - 2
src/converse-inverse.js

@@ -16,8 +16,7 @@
     ], factory);
 }(this, function (converse, tpl_brand_heading) {
     "use strict";
-    const $ = converse.env.jQuery,
-        { Strophe, _ } = converse.env;
+    const { Strophe, _ } = converse.env;
 
     function createBrandHeadingElement () {
         const div = document.createElement('div');

+ 3 - 4
src/converse-mam.js

@@ -9,16 +9,15 @@
 // XEP-0059 Result Set Management
 
 (function (root, factory) {
-    define([
+    define(["jquery.noconflict",
             "converse-core",
             "converse-chatview", // Could be made a soft dependency
             "converse-muc", // Could be made a soft dependency
             "strophe.rsm"
     ], factory);
-}(this, function (converse) {
+}(this, function ($, converse) {
     "use strict";
-    const $ = converse.env.jQuery,
-          { Strophe, $iq, _, moment } = converse.env;
+    const { Strophe, $iq, _, moment } = converse.env;
 
     const RSM_ATTRIBUTES = ['max', 'first', 'last', 'after', 'before', 'index', 'count'];
     // XEP-0313 Message Archive Management

+ 4 - 3
src/converse-minimize.js

@@ -7,7 +7,8 @@
 /*global define, window */
 
 (function (root, factory) {
-    define(["converse-core",
+    define(["jquery.noconflict",
+            "converse-core",
             "tpl!chatbox_minimize",
             "tpl!toggle_chats",
             "tpl!trimmed_chat",
@@ -17,6 +18,7 @@
             "converse-muc"
     ], factory);
 }(this, function (
+        $,
         converse,
         tpl_chatbox_minimize,
         tpl_toggle_chats,
@@ -25,8 +27,7 @@
     ) {
     "use strict";
 
-    const $ = converse.env.jQuery,
-          { _ , utils, Backbone, b64_sha1, moment } = converse.env;
+    const { _ , utils, Backbone, b64_sha1, moment } = converse.env;
 
     converse.plugins.add('converse-minimize', {
         overrides: {

+ 2 - 1
src/converse-muc.js

@@ -11,6 +11,7 @@
  */
 (function (root, factory) {
     define([
+            "jquery.noconflict",
             "converse-core",
             "tpl!chatarea",
             "tpl!chatroom",
@@ -34,6 +35,7 @@
             "converse-chatview"
     ], factory);
 }(this, function (
+            $,
             converse,
             tpl_chatarea,
             tpl_chatroom,
@@ -61,7 +63,6 @@
     const CHATROOMS_TYPE = 'chatroom';
 
     const { Strophe, Backbone, $iq, $build, $msg, $pres, b64_sha1, sizzle, utils, _, fp, moment } = converse.env;
-    const $ = converse.env.jQuery;
 
     // Add Strophe Namespaces
     Strophe.addNamespace('MUC_ADMIN', Strophe.NS.MUC + "#admin");

+ 3 - 3
src/converse-otr.js

@@ -11,15 +11,15 @@
  */
 (function (root, factory) {
 
-    define(["converse-chatview",
+    define(["jquery.noconflict",
+            "converse-chatview",
             "tpl!toolbar_otr",
             'otr'
     ], factory);
-}(this, function (converse, tpl_toolbar_otr, otr) {
+}(this, function ($, converse, tpl_toolbar_otr, otr) {
     "use strict";
 
     const { Strophe, utils, b64_sha1, _ } = converse.env;
-    const $ = converse.env.jQuery;
 
     const HAS_CSPRNG = ((!_.isUndefined(crypto)) &&
         ((_.isFunction(crypto.randomBytes)) || (_.isFunction(crypto.getRandomValues))

+ 3 - 2
src/converse-register.js

@@ -10,7 +10,8 @@
  * as specified in XEP-0077.
  */
 (function (root, factory) {
-    define(["converse-core",
+    define(["jquery.noconflict",
+            "converse-core",
             "tpl!form_username",
             "tpl!register_panel",
             "tpl!register_tab",
@@ -20,6 +21,7 @@
             "converse-controlbox"
     ], factory);
 }(this, function (
+            $,
             converse,
             tpl_form_username,
             tpl_register_panel,
@@ -33,7 +35,6 @@
 
     // Strophe methods for building stanzas
     const { Strophe, Backbone, utils, $iq, _ } = converse.env;
-    const $ = converse.env.jQuery;
 
     // Add Strophe Namespaces
     Strophe.addNamespace('REGISTER', 'jabber:iq:register');

+ 16 - 13
src/converse-roomslist.js

@@ -10,15 +10,15 @@
  * rooms in the "Rooms Panel" of the ControlBox.
  */
 (function (root, factory) {
-    define(["utils",
+    define(["jquery.noconflict",
+            "utils",
             "converse-core",
             "converse-muc",
             "tpl!rooms_list",
             "tpl!rooms_list_item"
         ], factory);
-}(this, function (utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) {
-    const $ = converse.env.jQuery,
-          { Backbone, b64_sha1, sizzle, _ } = converse.env;
+}(this, function ($, utils, converse, muc, tpl_rooms_list, tpl_rooms_list_item) {
+    const { Backbone, Promise, b64_sha1, sizzle, _ } = converse.env;
 
     converse.plugins.add('converse-roomslist', {
         initialize () {
@@ -161,15 +161,18 @@
                 );
             };
 
-            $.when(_converse.api.waitUntil('chatBoxesFetched'),
-                   _converse.api.waitUntil('roomsPanelRendered')).then(
-                function () {
-                    if (_converse.allow_bookmarks) {
-                        _converse.api.waitUntil('bookmarksInitialized').then(initRoomsListView);
-                    } else {
-                        initRoomsListView();
-                    }
-                });
+            Promise.all([
+                _converse.api.waitUntil('chatBoxesFetched'),
+                _converse.api.waitUntil('roomsPanelRendered')
+            ]).then(() => {
+                if (_converse.allow_bookmarks) {
+                    _converse.api.waitUntil('bookmarksInitialized').then(
+                        initRoomsListView
+                    );
+                } else {
+                    initRoomsListView();
+                }
+            });
 
             const afterReconnection = function () {
                 if (_.isUndefined(_converse.rooms_list_view)) {

+ 4 - 3
src/converse-rosterview.js

@@ -7,7 +7,8 @@
 /*global define */
 
 (function (root, factory) {
-    define(["converse-core",
+    define(["jquery.noconflict",
+            "converse-core",
             "tpl!group_header",
             "tpl!pending_contact",
             "tpl!requesting_contact",
@@ -16,6 +17,7 @@
             "tpl!roster_item"
     ], factory);
 }(this, function (
+            $,
             converse, 
             tpl_group_header,
             tpl_pending_contact,
@@ -24,8 +26,7 @@
             tpl_roster_filter,
             tpl_roster_item) {
     "use strict";
-    const $ = converse.env.jQuery,
-          { Backbone, utils, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
+    const { Backbone, utils, Strophe, $iq, b64_sha1, sizzle, _ } = converse.env;
 
 
     converse.plugins.add('converse-rosterview', {

+ 3 - 4
src/converse-vcard.js

@@ -7,11 +7,10 @@
 /*global define */
 
 (function (root, factory) {
-    define(["converse-core", "strophe.vcard"], factory);
-}(this, function (converse) {
+    define(["jquery.noconflict", "converse-core", "strophe.vcard"], factory);
+}(this, function ($, converse) {
     "use strict";
-    const { Strophe, _, moment } = converse.env,
-          $ = converse.env.jQuery;
+    const { Strophe, _, moment } = converse.env;
 
     converse.plugins.add('converse-vcard', {
 

+ 14 - 2
src/utils.js

@@ -3,6 +3,7 @@
     define([
         "jquery.noconflict",
         "sizzle",
+        "es6-promise",
         "jquery.browser",
         "lodash.noconflict",
         "locales",
@@ -18,7 +19,9 @@
         "tpl!form_captcha"
     ], factory);
 }(this, function (
-        $, sizzle, dummy, _,
+        $, sizzle,
+        Promise,
+        dummy, _,
         locales,
         moment,
         Strophe,
@@ -524,7 +527,16 @@
 
     utils.isPersistableModel = function (model) {
         return model.collection && model.collection.browserStorage;
-    }
+    };
+
+    utils.getWrappedPromise = function () {
+        const wrapper = {};
+        wrapper.promise = new Promise((resolve, reject) => {
+            wrapper.resolve = resolve;
+            wrapper.reject = reject;
+        })
+        return wrapper;
+    };
 
     utils.safeSave = function (model, attributes) {
         if (utils.isPersistableModel(model)) {

+ 3 - 2
tests/mock.js

@@ -1,6 +1,6 @@
 (function (root, factory) {
-    define("mock", ['converse'], factory);
-}(this, function (converse_api) {
+    define("mock", ['jquery.noconflict', 'converse'], factory);
+}(this, function ($, converse_api) {
     var _ = converse_api.env._;
     var Strophe = converse_api.env.Strophe;
     var $iq = converse_api.env.$iq;
@@ -101,6 +101,7 @@
             'debug': false
         }, settings || {}));
         converse.ChatBoxViews.prototype.trimChat = function () {};
+        $.fx.off = true;
         return converse;
     }
 

+ 2 - 3
tests/utils.js

@@ -1,8 +1,7 @@
 (function (root, factory) {
-    define(['converse', 'es6-promise',  'mock', 'wait-until-promise'], factory);
-}(this, function (converse_api, Promise, mock, waitUntilPromise) {
+    define(['jquery.noconflict', 'converse', 'es6-promise',  'mock', 'wait-until-promise'], factory);
+}(this, function ($, converse_api, Promise, mock, waitUntilPromise) {
     var _ = converse_api.env._;
-    var $ = converse_api.env.jQuery;
     var $msg = converse_api.env.$msg;
     var $pres = converse_api.env.$pres;
     var $iq = converse_api.env.$iq;