Trying to use a document fragment for the roster view

This commit is contained in:
JC Brand 2014-07-25 09:58:42 +02:00
parent a9f9328682
commit cfa55896a5
2 changed files with 52 additions and 34 deletions

View File

@ -3266,7 +3266,8 @@
toggle_state: toggle_state toggle_state: toggle_state
}); });
} }
this.$el.hide().html(roster_markup); this.$fragment = $('<span>');
this.$fragment.append($(roster_markup));
}, },
onAdd: function (item) { onAdd: function (item) {
@ -3281,6 +3282,14 @@
} }
}, },
showRoster: function () {
if (this.$fragment) {
this.$el.html(this.$fragment)
delete this.$fragment;
}
return this;
},
onChange: function (item) { onChange: function (item) {
if ((_.size(item.changed) === 1) && _.contains(_.keys(item.changed), 'sorted')) { if ((_.size(item.changed) === 1) && _.contains(_.keys(item.changed), 'sorted')) {
return; return;
@ -3342,6 +3351,17 @@
return this; return this;
}, },
getRosterElement: function () {
// TODO: see is _ensureElement can be used.
// Return the document fragment if it exists.
var $el;
if (this.$fragment) {
return this.$fragment;
} else {
return this.$el;
}
},
addRosterItem: function (item) { addRosterItem: function (item) {
if ((converse.show_only_online_users) && (item.get('chat_status') !== 'online')) { if ((converse.show_only_online_users) && (item.get('chat_status') !== 'online')) {
return this; return this;
@ -3352,13 +3372,14 @@
return this; return this;
} }
view.render() view.render()
var $el = this.getRosterElement();
if (view.$el.hasClass('current-xmpp-contact')) { if (view.$el.hasClass('current-xmpp-contact')) {
// TODO: need to add group support // TODO: need to add group support
this.$('.roster-group').after(view.el); $el.find('.roster-group').after(view.el);
} else if (view.$el.hasClass('pending-xmpp-contact')) { } else if (view.$el.hasClass('pending-xmpp-contact')) {
this.$('#pending-xmpp-contacts').after(view.el); $el.find('#pending-xmpp-contacts').after(view.el);
} else if (view.$el.hasClass('requesting-xmpp-contact')) { } else if (view.$el.hasClass('requesting-xmpp-contact')) {
this.$('#xmpp-contact-requests').after(view.render().el); $el.find('#xmpp-contact-requests').after(view.render().el);
} }
return this; return this;
}, },
@ -3379,9 +3400,10 @@
}, },
toggleHeaders: function () { toggleHeaders: function () {
var $contact_requests = this.$('#xmpp-contact-requests'), var $el = this.getRosterElement();
$pending_contacts = this.$('#pending-xmpp-contacts'); var $contact_requests = $el.find('#xmpp-contact-requests'),
var $groups = this.$el.find('.roster-group'); $pending_contacts = $el.find('#pending-xmpp-contacts');
var $groups = $el.find('.roster-group');
// Hide the headers if there are no contacts under them // Hide the headers if there are no contacts under them
_.each([$groups, $contact_requests, $pending_contacts], function (h) { _.each([$groups, $contact_requests, $pending_contacts], function (h) {
var show_or_hide = function (h) { var show_or_hide = function (h) {
@ -3431,29 +3453,19 @@
* 1). See if the jquery detach method can be somehow used to avoid DOM reflows. * 1). See if the jquery detach method can be somehow used to avoid DOM reflows.
* 2). Likewise see if documentFragment can be used. * 2). Likewise see if documentFragment can be used.
*/ */
this.$el.find('.roster-group').each($.proxy(function (idx, group) { var $el = this.getRosterElement();
$el.find('.roster-group').each($.proxy(function (idx, group) {
var $group = $(group); var $group = $(group);
var $contacts = $group.nextUntil('dt', 'dd.current-xmpp-contact'); var $contacts = $group.nextUntil('dt', 'dd.current-xmpp-contact');
$group.after($contacts.tsort({sortFunction: this.sortFunction, data: 'status'}, 'a')); $group.after($contacts.tsort({sortFunction: this.sortFunction, data: 'status'}, 'a'));
},this)); },this));
// Also sort pending and requesting contacts // Also sort pending and requesting contacts
var crit = {order:'asc'}, var crit = {order:'asc'},
$contact_requests = this.$('#xmpp-contact-requests'), $contact_requests = $el.find('#xmpp-contact-requests'),
$pending_contacts = this.$('#pending-xmpp-contacts'); $pending_contacts = $el.find('#pending-xmpp-contacts');
$pending_contacts.after($pending_contacts.siblings('dd.pending-xmpp-contact').tsort(crit)); $pending_contacts.after($pending_contacts.siblings('dd.pending-xmpp-contact').tsort(crit));
$contact_requests.after($contact_requests.siblings('dd.requesting-xmpp-contact').tsort(crit)); $contact_requests.after($contact_requests.siblings('dd.requesting-xmpp-contact').tsort(crit));
return this; return this;
},
showRoster: function () {
if (!this.$el.is(':visible')) {
// Once all initial roster items have been added, we
// can show the roster.
// TODO: It would be more efficient to use a
// documentFragment and then put that in the DOM
this.$el.show();
}
return this;
} }
}); });

View File

@ -129,28 +129,32 @@
describe("The Contacts Roster", $.proxy(function (mock, utils) { describe("The Contacts Roster", $.proxy(function (mock, utils) {
describe("Pending Contacts", $.proxy(function () { describe("Pending Contacts", $.proxy(function () {
beforeEach($.proxy(function () { function _clearContacts () {
runs(function () { utils.clearBrowserStorage();
converse.rosterview.model.reset(); converse.rosterview.model.reset();
utils.createContacts('pending').openControlBox(); converse.rosterview.initialize();
}); };
waits(50);
runs(function () { function _addContacts () {
utils.openContactsPanel(); _clearContacts();
}); // Must be initialized, so that render is called and documentFragment set up.
}, converse)); utils.createContacts('pending').openControlBox();
utils.openContactsPanel();
};
it("do not have a header if there aren't any", $.proxy(function () { it("do not have a header if there aren't any", $.proxy(function () {
converse.rosterview.model.reset(); _addContacts();
this.rosterview.model.reset();
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 collapsed under their own header", $.proxy(function () { it("can be collapsed under their own header", $.proxy(function () {
_addContacts();
checkHeaderToggling.apply(this, [this.rosterview.$el.find('dt#pending-xmpp-contacts')]); checkHeaderToggling.apply(this, [this.rosterview.$el.find('dt#pending-xmpp-contacts')]);
}, converse)); }, converse));
it("can be added to the roster", $.proxy(function () { it("can be added to the roster", $.proxy(function () {
converse.rosterview.model.reset(); // We want to manually create users so that we can spy _clearContacts();
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(this.rosterview, 'updateRoster').andCallThrough(); spyOn(this.rosterview, 'updateRoster').andCallThrough();
runs($.proxy(function () { runs($.proxy(function () {
@ -171,6 +175,7 @@
}, converse)); }, converse));
it("can be removed by the user", $.proxy(function () { it("can be removed by the user", $.proxy(function () {
_addContacts();
var jid = mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; var jid = mock.pend_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
var view = this.rosterview.get(jid); var view = this.rosterview.get(jid);
spyOn(window, 'confirm').andReturn(true); spyOn(window, 'confirm').andReturn(true);
@ -190,6 +195,7 @@
}, converse)); }, converse));
it("will lose their own header once the last one has been removed", $.proxy(function () { it("will lose their own header once the last one has been removed", $.proxy(function () {
_addContacts();
var view; var view;
spyOn(window, 'confirm').andReturn(true); spyOn(window, 'confirm').andReturn(true);
for (i=0; i<mock.pend_names.length; i++) { for (i=0; i<mock.pend_names.length; i++) {
@ -200,7 +206,7 @@
}, converse)); }, 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 () {
converse.rosterview.model.reset(); // We want to manually create users so that we can spy _clearContacts();
var i, t, is_last; var i, t, is_last;
spyOn(converse, 'emit'); spyOn(converse, 'emit');
spyOn(this.rosterview, 'updateRoster').andCallThrough(); spyOn(this.rosterview, 'updateRoster').andCallThrough();