');
break;
case 'j':
case 'join':
case 'J':
this.addJoinLeave('join', row[1], null, row[0] === 'J');
break;
case 'l':
case 'leave':
case 'L':
this.addJoinLeave('leave', row[1], null, row[0] === 'L');
break;
case 'n':
case 'N':
this.addJoinLeave('rename', row[1], row[2], true);
break;
case 'refresh':
// refresh the page
document.location.reload(true);
break;
case 'users':
this.parseUserList(row[1]);
break;
case 'raw':
this.$chat.append('
');
} else {
// Normal chat message.
if (message.substr(0,2) === '//') message = message.substr(1);
this.$chat.append(chatDiv + timestamp + '' + clickableName + ':' + messageSanitize(message) + '');
}
},
getHighlight: function(message) {
var highlights = Tools.prefs('highlights') || [];
if (!app.highlightRegExp) {
try {
app.highlightRegExp = new RegExp('\\b('+highlights.join('|')+')\\b', 'i');
} catch (e) {
// If the expression above is not a regexp, we'll get here.
// Don't throw an exception because that would prevent the chat
// message from showing up, or, when the lobby is initialising,
// it will prevent the initialisation from completing.
return false;
}
}
return ((highlights.length > 0) && app.highlightRegExp.test(message));
},
parseBattleID: function(id) {
if (id.lastIndexOf('-') > 6) {
return id.match(/^battle\-([a-z0-9]*)\-?[0-9]*$/);
}
return id.match(/^battle\-([a-z0-9]*[a-z])[0-9]*$/);
},
// tab completion
initializeTabComplete: function() {
this.tabComplete = {
candidates: null,
index: 0,
prefix: null,
cursor: -1,
reset: function() {
this.cursor = -1;
}
};
this.userActivity = [];
},
markUserActive: function(userid) {
var idx = this.userActivity.indexOf(userid);
if (idx !== -1) {
this.userActivity.splice(idx, 1);
}
this.userActivity.push(userid);
if (this.userActivity.length > 100) {
// Prune the list.
this.userActivity.splice(0, 20);
}
},
tabComplete: null,
userActivity: null,
handleTabComplete: function($textbox) {
// Don't tab complete at the start of the text box.
var idx = $textbox.prop('selectionStart');
if (idx === 0) return false;
var text = $textbox.val();
if (idx === this.tabComplete.cursor) {
// The user is cycling through the candidate names.
if (++this.tabComplete.index >= this.tabComplete.candidates.length) {
this.tabComplete.index = 0;
}
} else {
// This is a new tab completion.
// There needs to be non-whitespace to the left of the cursor.
var m = /^(.*?)([^ ]*)$/.exec(text.substr(0, idx));
if (!m) return false;
this.tabComplete.prefix = m[1];
var idprefix = toId(m[2]);
var candidates = [];
for (var i in this.users) {
if (i.substr(0, idprefix.length) === idprefix) {
candidates.push(i);
}
}
// Sort by most recent to speak in the chat, or, in the case of a tie,
// in alphabetical order.
var self = this;
candidates.sort(function(a, b) {
var aidx = self.userActivity.indexOf(a);
var bidx = self.userActivity.indexOf(b);
if (aidx !== -1) {
if (bidx !== -1) {
return bidx - aidx;
}
return -1; // a comes first
} else if (bidx != -1) {
return 1; // b comes first
}
return (a < b) ? -1 : 1; // alphabetical order
});
self.tabComplete.candidates = candidates;
self.tabComplete.index = 0;
}
// Substitute in the tab-completed name.
var substituteUserId = this.tabComplete.candidates[this.tabComplete.index];
var name = this.users[substituteUserId].substr(1);
$textbox.val(this.tabComplete.prefix + name + text.substr(idx));
var pos = this.tabComplete.prefix.length + name.length;
$textbox[0].setSelectionRange(pos, pos);
this.tabComplete.cursor = pos;
return true;
}
}, {
getTimestamp: function(section) {
var pref = Tools.prefs('timestamps') || {};
var sectionPref = ((section === 'pms') ? pref.pms : pref.lobby) || 'off';
if ((sectionPref === 'off') || (sectionPref === undefined)) return '';
var date = new Date();
var components = [ date.getHours(), date.getMinutes() ];
if (sectionPref === 'seconds') {
components.push(date.getSeconds());
}
return '[' + components.map(
function(x) { return (x < 10) ? '0' + x : x; }
).join(':') + '] ';
}
});
// user list
var UserList = this.UserList = Backbone.View.extend({
initialize: function(options) {
this.room = options.room;
},
construct: function() {
var buf = '';
buf += '
' + (this.room.userCount.users || '0') + ' users
';
var users = [];
if (this.room.users) {
var self = this;
users = Object.keys(this.room.users).sort(function(a, b) {
return self.comparator(a, b);
});
}
for (var i=0, len=users.length; i';
buf += 'Due to lag, ' + this.room.userCount.unregistered + ' unregistered users are hidden.';
buf += ' ';
buf += '';
buf += '';
}
if (this.room.userCount.guests) {
buf += '
';
}
this.$el.html(buf);
},
ranks: {
'~': 2,
'&': 2,
'@': 1,
'%': 1,
'+': 1,
' ': 0,
'!': 0,
'#': 0
},
rankOrder: {
'~': 1,
'&': 2,
'@': 3,
'%': 4,
'+': 5,
' ': 6,
'!': 7,
'#': 8
},
updateUserCount: function() {
$('#usercount-users').html(this.room.userCount.users || '0');
},
updateCurrentUser: function() {
$('.userlist > .cur').attr('class', '');
$('#userlist-user-' + me.userForm).attr('class', 'cur');
},
add: function(userid) {
var users = this.$el.children();
// Determine where to insert the user using a binary search.
var left = 0;
var right = users.length - 1;
while (right >= left) {
var mid = Math.floor((right - left) / 2 + left);
var cmp = this.elemComparator(users[mid], userid);
if (cmp < 0) {
left = mid + 1;
} else if (cmp > 0) {
right = mid - 1;
} else {
// The user is already in the list.
return;
}
}
$(this.constructItem(userid)).insertAfter($(users[right]));
},
remove: function(userid) {
$('#userlist-user-' + userid).remove();
},
buttonOnClick: function(userid) {
if (app.user.get('named')) {
return selfR.formChallenge(userid);
}
return selfR.formRename();
},
constructItem: function(userid) {
var name = this.room.users[userid];
var text = '';
// Sanitising the `userid` here is probably unnecessary, because
// IDs can't contain anything dangerous.
text += '
';
text += '';
text += '
';
return text;
},
elemComparator: function(elem, userid) {
var id = elem.id;
switch (id) {
case 'userlist-users':
return -1; // `elem` comes first
case 'userlist-empty':
case 'userlist-unregistered':
case 'userlist-guests':
return 1; // `userid` comes first
}
// extract the portion of the `id` after 'userlist-user-'
var elemuserid = id.substr(14);
return this.comparator(elemuserid, userid);
},
comparator: function(a, b) {
if (a === b) return 0;
var aRank = this.rankOrder[this.room.users[a] ? this.room.users[a].substr(0, 1) : ' '];
var bRank = this.rankOrder[this.room.users[b] ? this.room.users[b].substr(0, 1) : ' '];
if (aRank !== bRank) return aRank - bRank;
return (a > b ? 1 : -1);
},
noNamedUsersOnline: '
Only guests
',
updateNoUsersOnline: function() {
var elem = $('#userlist-empty');
if ($("[id^=userlist-user-]").length === 0) {
if (elem.length === 0) {
var guests = $('#userlist-guests');
if (guests.length === 0) {
this.$el.append($(this.noNamedUsersOnline));
} else {
guests.before($(this.noNamedUsersOnline));
}
}
} else {
elem.remove();
}
}
});
}).call(this, jQuery);