(function ($) {
function arrayToPhrase(array, finalSeparator) {
if (array.length <= 1)
return array.join();
finalSeparator = finalSeparator || "and";
return array.slice(0, -1).join(", ") + " " + finalSeparator + " " + array.slice(-1)[0];
}
function clampPosition(element, position) {
var $element = $(element);
var elementWidth = $element.width();
var parentWidth = $element.parent().width();
if (parentWidth >= elementWidth)
position.right = parentWidth / 2 - elementWidth / 2;
else if (position.right < parentWidth - elementWidth)
position.right = parentWidth - elementWidth;
else if (position.right > 0)
position.right = 0;
var elementHeight = $element.height();
var parentHeight = $element.parent().height();
if (parentHeight >= elementHeight)
position.top = parentHeight / 2 - elementHeight / 2;
else if (position.top < parentHeight - elementHeight)
position.top = parentHeight - elementHeight;
else if (position.top > 0)
position.top = 0;
}
function makeDraggable(element, popoutCallback, position) {
var $element = $(element);
position = position || {};
$element.css({
'user-select': 'none',
cursor: 'default',
position: 'absolute'
});
if (popoutCallback)
$element.parent().append($('Pop-out ').on('click', popoutCallback));
if (!('right' in position) || position.isDefault) {
position.right = $element.parent().width() / 2 - $element.width() / 2;
position.top = 0;
position.isDefault = true;
}
clampPosition(element, position);
$element.css({
right: position.right,
top: position.top
});
// Note: Origin starts from the top right instead of the top left here,
// because when a battle room is opened, the left side moves but the right doesn't,
// so we have to use the right side to keep the element in the same location.
var innerX = 0;
var innerY = 0;
$element.on('mousedown', function (e) {
innerX = e.pageX + ($element.parent().width() - $element.width() - this.offsetLeft);
innerY = e.pageY - this.offsetTop;
function mouseMoveCallback(e) {
position.right = innerX - e.pageX;
position.top = e.pageY - innerY;
delete position.isDefault;
clampPosition(element, position);
$element.css({
right: position.right,
top: position.top
});
}
$(document).on('mousemove', mouseMoveCallback)
.one('mouseup', function () {
$(document).off('mousemove', mouseMoveCallback);
});
});
}
this.TournamentBox = (function () {
function TournamentBox(room, $wrapper) {
this.room = room;
this.$wrapper = $wrapper;
$wrapper.html(
'
' +
'
Tournament' +
'
' +
'
Toggle
' +
'
' +
'');
this.$title = $wrapper.find('.tournament-title');
this.$format = $wrapper.find('.tournament-format');
this.$generator = $wrapper.find('.tournament-generator');
this.$status = $wrapper.find('.tournament-status');
this.$box = $wrapper.find('.tournament-box');
this.$bracket = $wrapper.find('.tournament-bracket');
this.$tools = $wrapper.find('.tournament-tools');
this.$join = $wrapper.find('.tournament-join');
this.$leave = $wrapper.find('.tournament-leave');
this.$noMatches = $wrapper.find('.tournament-nomatches');
this.$teamSelect = $wrapper.find('.tournament-team');
this.$validate = $wrapper.find('.tournament-validate');
this.$challenge = $wrapper.find('.tournament-challenge');
this.$challengeUser = $wrapper.find('.tournament-challenge-user');
this.$challengeUserMenu = $wrapper.find('.tournament-challenge-user-menu');
this.$challengeChallenge = $wrapper.find('.tournament-challenge-challenge');
this.$challengeBy = $wrapper.find('.tournament-challengeby');
this.$challenging = $wrapper.find('.tournament-challenging');
this.$challengingMessage = $wrapper.find('.tournament-challenging-message');
this.$challenged = $wrapper.find('.tournament-challenged');
this.$challengedMessage = $wrapper.find('.tournament-challenged-message');
this.$challengeAccept = $wrapper.find('.tournament-challenge-accept');
this.$challengeCancel = $wrapper.find('.tournament-challenge-cancel');
this.info = {};
this.updates = {};
this.savedBracketPosition = {};
this.$lastJoinLeaveMessage = null;
this.batchedJoins = [];
this.batchedLeaves = [];
this.bracketPopup = null;
this.savedPopoutBracketPosition = {};
var self = this;
this.$title.on('click', function () {
self.toggleBoxVisibility();
});
this.$box.on('transitionend webkitTransitionEnd oTransitionEnd otransitionend', function () {
if (self.isBoxVisible())
self.$box.css('transition', 'none');
if (!self.info.isActive)
self.$wrapper.find('.active').andSelf().removeClass('active');
});
this.$join.on('click', function () {
self.room.send('/tournament join');
});
this.$leave.on('click', function () {
self.room.send('/tournament leave');
});
this.$challengeChallenge.on('click', function () {
app.sendTeam(Storage.teams[self.$teamSelect.children().val()]);
self.room.send('/tournament challenge ' + self.$challengeUserMenu.children().val());
});
this.$challengeAccept.on('click', function () {
app.sendTeam(Storage.teams[self.$teamSelect.children().val()]);
self.room.send('/tournament acceptchallenge');
});
this.$challengeCancel.on('click', function () {
self.room.send('/tournament cancelchallenge');
});
this.$validate.on('click', function () {
app.sendTeam(Storage.teams[self.$teamSelect.children().val()]);
self.room.send('/tournament vtm');
});
app.user.on('saveteams', this.updateTeams, this);
}
TournamentBox.prototype.updateLayout = function () {
this.$box.css('max-height', this.isBoxVisible() ? this.$box[0].scrollHeight : '');
if (this.$bracket.hasClass('tournament-bracket-overflowing')) {
clampPosition(this.$bracket.children().first(), this.savedBracketPosition);
this.$bracket.children().first().css({
right: this.savedBracketPosition.right,
top: this.savedBracketPosition.top
});
} else {
if (this.$bracket[0].offsetHeight < this.$bracket[0].scrollHeight ||
this.$bracket[0].offsetWidth < this.$bracket[0].scrollWidth) {
this.$bracket.addClass('tournament-bracket-overflowing');
makeDraggable(this.$bracket.children().first(), this.showBracketPopup.bind(this, this.info.bracketData, false), this.savedBracketPosition);
}
}
};
TournamentBox.prototype.updateTeams = function () {
var forceFormatChange = (this.info.teambuilderFormat !== this.teamSelectFormat);
this.teamSelectFormat = this.info.teambuilderFormat;
if (!this.info.isJoined) {
this.$teamSelect.hide();
if (forceFormatChange) this.$teamSelect.html('');
return;
}
var teamIndex = -1;
if (!forceFormatChange && this.$teamSelect.children().val()) {
teamIndex = parseInt(this.$teamSelect.children().val(), 10);
if (isNaN(teamIndex)) teamIndex = -1;
}
this.$teamSelect.html(app.rooms[''].renderTeams.call(this, this.info.teambuilderFormat, teamIndex));
this.$teamSelect.children().data('type', 'teamSelect');
this.$teamSelect.children().attr('name', 'tournamentButton');
this.$teamSelect.show();
var val = this.$teamSelect.children().val();
// this.$validate.toggle(val !== 'random');
this.$validate.toggleClass('disabled', !val || !val.length || val === 'random');
};
TournamentBox.prototype.isBoxVisible = function () {
return this.$box.hasClass('active');
};
TournamentBox.prototype.toggleBoxVisibility = function (isVisible) {
var isCurrentlyVisible = this.isBoxVisible();
if (isVisible === undefined)
isVisible = !isCurrentlyVisible;
if ((isVisible && isCurrentlyVisible) ||
(!isVisible && !isCurrentlyVisible))
return;
this.$box.toggleClass('active', !!isVisible);
this.$box.css('transition', '');
this.$box.css('max-height', isVisible ? this.$box[0].scrollHeight : '');
};
TournamentBox.prototype.parseMessage = function (data, isBroadcast) {
var cmd = data.shift().toLowerCase();
if (isBroadcast) {
switch (cmd) {
case 'info':
var tournaments = JSON.parse(data.join('|'));
var $infoList = "No tournaments are currently running.";
if (tournaments.length > 0) {
$infoList = $('');
tournaments.forEach(function (tournament) {
var formatName = window.BattleFormats && BattleFormats[tournament.format] ? BattleFormats[tournament.format].name : tournament.format;
var $info = $(' ');
$info.text(": " + formatName + " " + tournament.generator + (tournament.isStarted ? " (Started)" : ""));
$info.prepend($(' ').attr('href', app.root + toRoomid(tournament.room).toLowerCase()).text(tournament.room));
$infoList.append($info);
});
}
this.room.$chat.append($('').append($infoList)));
break;
default:
return true;
}
} else {
switch (cmd) {
case 'create':
var formatName = window.BattleFormats && BattleFormats[data[0]] ? BattleFormats[data[0]].name : data[0];
var type = data[1];
this.room.$chat.append("" + BattleLog.escapeHTML(formatName) + " " + BattleLog.escapeHTML(type) + " Tournament created.
");
this.room.notifyOnce("Tournament created", "Room: " + this.room.title + "\nFormat: " + formatName + "\nType: " + type, 'tournament-create');
this.curTeamIndex = 0;
this.updateTeams();
break;
case 'join':
case 'leave':
if (this.$lastJoinLeaveMessage && !this.$lastJoinLeaveMessage.is(this.room.$chat.children().last())) {
this.$lastJoinLeaveMessage = null;
this.batchedJoins = [];
this.batchedLeaves = [];
}
if (!this.$lastJoinLeaveMessage) {
this.$lastJoinLeaveMessage = $('
');
this.room.$chat.append(this.$lastJoinLeaveMessage);
}
if (cmd === 'join' && this.batchedJoins.indexOf(data[0]) < 0) {
this.batchedJoins.push(data[0]);
}
if (cmd === 'leave' && this.batchedLeaves.indexOf(data[0]) < 0) {
this.batchedLeaves.push(data[0]);
}
var message = [];
var joins = this.batchedJoins.slice(0, 5);
var leaves = this.batchedLeaves.slice(0, 5);
if (this.batchedJoins.length > 5) joins.push((this.batchedJoins.length - 5) + " others");
if (this.batchedLeaves.length > 5) leaves.push((this.batchedLeaves.length - 5) + " others");
if (joins.length > 0) message.push(arrayToPhrase(joins) + " joined the tournament");
if (leaves.length > 0) message.push(arrayToPhrase(leaves) + " left the tournament");
this.$lastJoinLeaveMessage.text(message.join("; ") + ".");
break;
case 'start':
this.room.closeNotification('tournament-create');
if (!this.info.isJoined) {
this.toggleBoxVisibility(false);
} else if (this.info.teambuilderFormat.substr(0, 4) === 'gen5' && !Dex.loadedSpriteData['bw']) {
Dex.loadSpriteData('bw');
}
this.room.$chat.append("The tournament has started!
");
break;
case 'disqualify':
this.room.$chat.append("" + BattleLog.escapeHTML(data[0]) + " has been disqualified from the tournament.
");
break;
case 'autodq':
if (data[0] === 'off') {
this.room.$chat.append("The tournament's automatic disqualify timer has been turned off.
");
} else if (data[0] === 'on') {
var minutes = (data[1] / 1000 / 60);
this.room.$chat.append("The tournament's automatic disqualify timer has been set to " + minutes + " minute" + (minutes === 1 ? "" : "s") + ".
");
} else {
var seconds = Math.floor(data[1] / 1000);
app.addPopupMessage("Please respond to the tournament within " + seconds + " seconds or you may be automatically disqualified.");
this.room.notifyOnce("Tournament Automatic Disqualification Warning", "Room: " + this.room.title + "\nSeconds: " + seconds, 'tournament-autodq-warning');
}
break;
case 'autostart':
if (data[0] === 'off') {
this.room.$chat.append("The tournament's automatic start is now off.
");
} else if (data[0] === 'on') {
var minutes = (data[1] / 1000 / 60);
this.room.$chat.append("The tournament will automatically start in " + minutes + " minute" + (minutes === 1 ? "" : "s") + ".
");
}
break;
case 'scouting':
if (data[0] === 'allow') {
this.room.$chat.append("Scouting is now allowed (Tournament players can watch other tournament battles)
");
} else if (data[0] === 'disallow') {
this.room.$chat.append("Scouting is now banned (Tournament players can't watch other tournament battles)
");
}
break;
case 'update':
$.extend(this.updates, JSON.parse(data.join('|')));
break;
case 'updateend':
$.extend(this.info, this.updates);
if (!this.info.isActive) {
this.$wrapper.addClass("active");
if (!this.info.isStarted || this.info.isJoined)
this.toggleBoxVisibility(true);
this.info.isActive = true;
}
if ('format' in this.updates || 'teambuilderFormat' in this.updates) {
if (!this.info.teambuilderFormat) this.info.teambuilderFormat = this.info.format;
this.$format.text(window.BattleFormats && BattleFormats[this.info.format] ? BattleFormats[this.info.format].name : this.info.format);
this.updateTeams();
}
if ('isJoined' in this.updates) {
this.updateTeams();
}
if ('generator' in this.updates)
this.$generator.text(this.info.generator);
if ('isStarted' in this.updates) {
this.$status.text(this.info.isStarted ? "In Progress" : "Signups");
}
// Update the toolbox
if ('isStarted' in this.updates || 'isJoined' in this.updates) {
this.$join.toggleClass('active', !this.info.isStarted && !this.info.isJoined);
this.$leave.toggleClass('active', !this.info.isStarted && this.info.isJoined);
this.$validate.toggleClass('active', this.info.isJoined && !this.info.challenging && !this.info.challenged && !(this.info.challenges && this.info.challenges.length));
this.$tools.toggleClass('active', !this.info.isStarted || this.info.isJoined);
}
// Update the bracket
if ('bracketData' in this.updates) {
var $bracket = this.generateBracket(this.info.bracketData);
this.$bracket.empty();
this.$bracket.removeClass('tournament-bracket-overflowing');
if ($bracket) {
this.$bracket.append($bracket);
this.updateLayout();
if (this.bracketPopup)
this.bracketPopup.updateBracket(this.generateBracket(this.info.bracketData));
}
}
if (this.info.isStarted && this.info.isJoined) {
// Update the challenges
if ('challenges' in this.updates) {
if (this.info.challenges.length > 0) {
this.$challengeUser.text("vs. " + this.info.challenges[0]);
this.$challengeUserMenu.toggle(this.info.challenges.length > 1);
this.$challengeUserMenu.html(this.renderChallengeUsers());
this.toggleBoxVisibility(true);
if (!this.$challenge.hasClass('active')) {
this.room.notifyOnce("Tournament challenges available", "Room: " + this.room.title, 'tournament-challenges');
}
}
this.$challenge.toggleClass('active', this.info.challenges.length > 0);
}
if ('challengeBys' in this.updates) {
this.$challengeBy.toggleClass('active', this.info.challengeBys.length > 0);
if (this.info.challengeBys.length > 0)
this.$challengeBy.text((this.info.challenges.length > 0 ? "Or wait" : "Waiting") + " for " + arrayToPhrase(this.info.challengeBys, "or") + " to challenge you.");
}
if ('challenging' in this.updates) {
this.$challenging.toggleClass('active', !!this.info.challenging);
if (this.info.challenging) {
this.$challengingMessage.text("Waiting for " + this.info.challenging + "...");
}
}
if ('challenged' in this.updates) {
if (this.info.challenged) {
this.$challengedMessage.text("vs. " + this.info.challenged);
this.toggleBoxVisibility(true);
if (!this.$challenged.hasClass('active')) {
this.room.notifyOnce("Tournament challenge from " + this.info.challenged, "Room: " + this.room.title, 'tournament-challenged');
}
}
this.$challenged.toggleClass('active', !!this.info.challenged);
}
this.$noMatches.toggleClass('active',
this.info.challenges.length === 0 && this.info.challengeBys.length === 0 && !this.info.challenging && !this.info.challenged);
}
this.updates = {};
break;
case 'battlestart':
var roomid = toRoomid(data[2]).toLowerCase();
this.room.$chat.append('');
break;
case 'battleend':
var result = "drawn";
if (data[2] === 'win')
result = "won";
else if (data[2] === 'loss')
result = "lost";
var message = BattleLog.escapeHTML(data[0]) + " has " + result + " the match " + BattleLog.escapeHTML(data[3].split(',').join(' - ')) + " against " + BattleLog.escapeHTML(data[1]) +
(data[4] === 'fail' ? " but the tournament does not support drawing, so it did not count" : "") + ".";
var $battleMessage = data[5] ? this.room.$chat.find('.tournament-' + toRoomid(data[5]).toLowerCase()) : '';
if ($battleMessage && $battleMessage.length) {
$battleMessage.removeClass('tournament-message-battlestart').addClass('tournament-message-battleend').find('a').html(message);
} else {
this.room.$chat.append('' + message + '
');
}
break;
case 'end':
var endData = JSON.parse(data[0]);
var $bracket = this.generateBracket(endData.bracketData);
if ($bracket) {
var $bracketMessage = $('
').append($bracket);
this.room.$chat.append($bracketMessage);
if ($bracketMessage[0].offsetHeight < $bracketMessage[0].scrollHeight ||
$bracketMessage[0].offsetWidth < $bracketMessage[0].scrollWidth) {
$bracketMessage.addClass('tournament-message-end-bracket-overflowing');
makeDraggable($bracket, this.showBracketPopup.bind(this, endData.bracketData, true));
}
}
var type = endData.generator;
this.room.$chat.append("Congratulations to " + BattleLog.escapeHTML(arrayToPhrase(endData.results[0])) + " for winning the " + BattleLog.escapeFormat(endData.format) + " " + BattleLog.escapeHTML(type) + " Tournament!
");
if (endData.results[1])
this.room.$chat.append("Runner-up" + (endData.results[1].length > 1 ? "s" : "") + ": " + BattleLog.escapeHTML(arrayToPhrase(endData.results[1])) + "
");
// Fallthrough
case 'forceend':
this.room.closeNotification('tournament-create');
this.info = {};
this.updates = {};
this.savedBracketPosition = {};
if (this.bracketPopup)
this.bracketPopup.close();
this.savedPopoutBracketPosition = {};
if (!this.isBoxVisible() || app.curSideRoom !== this.room)
this.$wrapper.find('.active').andSelf().removeClass('active');
else
this.toggleBoxVisibility(false);
if (cmd === 'forceend')
this.room.$chat.append("The tournament was forcibly ended.
");
break;
case 'error':
var appendError = function (message) {
this.room.$chat.append("" + BattleLog.sanitizeHTML(message) + "
");
}.bind(this);
switch (data[0]) {
case 'BracketFrozen':
case 'AlreadyStarted':
appendError("The tournament has already started.");
break;
case 'BracketNotFrozen':
case 'NotStarted':
appendError("The tournament hasn't started yet.");
break;
case 'UserAlreadyAdded':
appendError("You are already in the tournament.");
break;
case 'AltUserAlreadyAdded':
appendError("One of your alts is already in the tournament.");
break;
case 'UserNotAdded':
appendError((data[1] && data[1] === app.user.get('userid') ? "You aren't" : "This user isn't") + " in the tournament.");
break;
case 'NotEnoughUsers':
appendError("There aren't enough users.");
break;
case 'InvalidAutoDisqualifyTimeout':
case 'InvalidAutoStartTimeout':
appendError("That isn't a valid timeout value.");
break;
case 'InvalidMatch':
appendError("That isn't a valid tournament matchup.");
break;
case 'UserNotNamed':
appendError("You must have a name in order to join the tournament.");
break;
case 'Full':
appendError("The tournament is already at maximum capacity for users.");
break;
case 'AlreadyDisqualified':
appendError((data[1] && data[1] === app.user.get('userid') ? "You have" : "This user has") + " already been disqualified.");
break;
case 'Banned':
appendError("You are banned from entering tournaments.");
break;
default:
appendError("Unknown error: " + data[0]);
break;
}
break;
default:
return true;
}
this.$box.css('max-height', this.isBoxVisible() ? this.$box[0].scrollHeight : '');
}
};
TournamentBox.prototype.generateBracket = function (data) {
if (data.type === 'tree') {
var $div = $('
');
if (!data.rootNode) {
if (!('users' in data)) return;
var users = data.users.length;
if (users) $div.html('' + users + ' user' + (users !== 1 ? 's' : '') + ': ' + BattleLog.escapeHTML(data.users.join(", ")));
return $div;
}
var name = app.user.get('name');
var nodeSize = {
width: 150, height: 20,
radius: 5,
separationX: 30, separationY: 15
};
var nodesByDepth = [];
var stack = [{node: data.rootNode, depth: 0}];
while (stack.length > 0) {
var frame = stack.pop();
if (!nodesByDepth[frame.depth])
nodesByDepth.push(0);
++nodesByDepth[frame.depth];
frame.node.children.forEach(function (child) {
stack.push({node: child, depth: frame.depth + 1});
});
}
var maxDepth = nodesByDepth.length;
var maxWidth = 0;
nodesByDepth.forEach(function (nodes) {
if (nodes > maxWidth)
maxWidth = nodes;
});
nodeSize.realWidth = nodeSize.width + nodeSize.radius * 2;
nodeSize.realHeight = nodeSize.height + nodeSize.radius * 2;
nodeSize.smallRealHeight = nodeSize.height / 2 + nodeSize.radius * 2;
var size = {
width: nodeSize.realWidth * maxDepth + nodeSize.separationX * maxDepth,
height: nodeSize.realHeight * (maxWidth + 0.5) + nodeSize.separationY * maxWidth
};
var tree = d3.layout.tree()
.size([size.height, size.width - nodeSize.realWidth - nodeSize.separationX])
.separation(function () { return 1; })
.children(function (node) {
return node.children.length === 0 ? null : node.children;
});
var nodes = tree.nodes(data.rootNode);
var links = tree.links(nodes);
var layoutRoot = d3.select($div[0])
.append('svg:svg').attr('width', size.width).attr('height', size.height)
.append('svg:g')
.attr('transform', 'translate(' + (-(nodeSize.realWidth + nodeSize.separationX) / 2) + ',0)');
var link = d3.svg.diagonal()
.source(function (link) {
return {x: link.source.x, y: link.source.y + nodeSize.realWidth / 2};
})
.target(function (link) {
return {x: link.target.x, y: link.target.y - nodeSize.realWidth / 2};
})
.projection(function (link) {
return [size.width - link.y, link.x];
});
layoutRoot.selectAll('path.tournament-bracket-tree-link').data(links).enter()
.append('svg:path')
.attr('d', link)
.classed('tournament-bracket-tree-link', true)
.classed('tournament-bracket-tree-link-active', function (link) {
return link.source.state === 'finished' && link.source.team === link.target.team;
});
var nodeGroup = layoutRoot.selectAll('g.tournament-bracket-tree-node').data(nodes).enter()
.append('svg:g').classed('tournament-bracket-tree-node', true).attr('transform', function (node) {
return 'translate(' + (size.width - node.y) + ',' + node.x + ')';
});
nodeGroup.append('svg:rect')
.attr('rx', nodeSize.radius)
.attr('x', -nodeSize.realWidth / 2).attr('width', nodeSize.realWidth)
.each(function (node) {
var elem = d3.select(this);
if (node.children.length === 0)
elem.attr('y', -nodeSize.smallRealHeight / 2).attr('height', nodeSize.smallRealHeight);
else
elem.attr('y', -nodeSize.realHeight / 2).attr('height', nodeSize.realHeight);
if (node.team === name) elem.attr('stroke-dasharray', '5,5');
});
nodeGroup.each(function (node) {
var elem = d3.select(this);
if (node.children.length === 0) {
elem.classed('tournament-bracket-tree-node-team', true);
elem.append('svg:text').text(node.team || "Unavailable");
} else {
elem.classed('tournament-bracket-tree-node-match', true);
elem.classed('tournament-bracket-tree-node-match-' + node.state, true);
if (node.state === 'unavailable')
elem.append('svg:text').text("Unavailable");
else {
var teams = elem.append('svg:text').attr('y', -nodeSize.realHeight / 5).classed('tournament-bracket-tree-node-match-teams', true);
var teamA = teams.append('svg:tspan').classed('tournament-bracket-tree-node-match-team', true).text(node.children[0].team);
teams.append('svg:tspan').text(" vs ");
var teamB = teams.append('svg:tspan').classed('tournament-bracket-tree-node-match-team', true).text(node.children[1].team);
var score = elem.append('svg:text').attr('y', nodeSize.realHeight / 5);
if (node.state === 'available')
score.text("Waiting");
else if (node.state === 'challenging')
score.text("Challenging");
else if (node.state === 'inprogress')
score.append('svg:a').attr('xlink:href', app.root + toRoomid(node.room).toLowerCase()).classed('ilink', true).text("In-progress").on('click', function () {
var e = d3.event;
if (e.cmdKey || e.metaKey || e.ctrlKey) return;
e.preventDefault();
e.stopPropagation();
var roomid = $(e.currentTarget).attr('href').substr(app.root.length);
app.tryJoinRoom(roomid);
});
else if (node.state === 'finished') {
if (node.result === 'win') {
teamA.classed('tournament-bracket-tree-node-match-team-win', true);
teamB.classed('tournament-bracket-tree-node-match-team-loss', true);
} else if (node.result === 'loss') {
teamA.classed('tournament-bracket-tree-node-match-team-loss', true);
teamB.classed('tournament-bracket-tree-node-match-team-win', true);
} else {
teamA.classed('tournament-bracket-tree-node-match-team-draw', true);
teamB.classed('tournament-bracket-tree-node-match-team-draw', true);
}
elem.classed('tournament-bracket-tree-node-match-result-' + node.result, true);
score.text(node.score.join(" - "));
}
}
}
if (node.parent && node.parent.state === 'finished')
if (node.parent.result === 'draw')
elem.classed('tournament-bracket-tree-node-draw', true);
else if (node.team === node.parent.team)
elem.classed('tournament-bracket-tree-node-win', true);
else
elem.classed('tournament-bracket-tree-node-loss', true);
});
return $div;
} else if (data.type === 'table') {
if (data.tableContents.length === 0)
return;
var $table = $('');
var $colHeaders = $(' ');
$table.append($colHeaders);
data.tableHeaders.cols.forEach(function (name) {
$colHeaders.append($(' ').text(name));
});
data.tableHeaders.rows.forEach(function (name, r) {
var $row = $(' ');
$table.append($row);
$row.append($(' ').text(name));
data.tableContents[r].forEach(function (cell) {
var $cell = $(' ');
$row.append($cell);
if (!cell) {
$cell.addClass('tournament-bracket-table-cell-null');
return;
}
$cell.addClass('tournament-bracket-table-cell-' + cell.state);
if (cell.state === 'unavailable')
$cell.text("Unavailable");
else if (cell.state === 'available')
$cell.text("Waiting");
else if (cell.state === 'challenging')
$cell.text("Challenging");
else if (cell.state === "inprogress")
$cell.html('In-progress ').children().on('click', function (e) {
if (e.cmdKey || e.metaKey || e.ctrlKey) return;
e.preventDefault();
e.stopPropagation();
var roomid = $(e.currentTarget).attr('href').substr(app.root.length);
app.tryJoinRoom(roomid);
});
else if (cell.state === 'finished') {
$cell.addClass('tournament-bracket-table-cell-result-' + cell.result);
$cell.text(cell.score.join(" - "));
}
});
$row.append($(' ').text(data.scores[r]));
});
return $table;
}
};
TournamentBox.prototype.showBracketPopup = function (data, isStandalone) {
if (isStandalone)
app.addPopup(BracketPopup, {$bracket: this.generateBracket(data)});
else
this.bracketPopup = app.addPopup(BracketPopup, {parent: this, $bracket: this.generateBracket(data)});
};
TournamentBox.prototype.renderChallengeUsers = function () {
return ' Change opponent ';
};
TournamentBox.prototype.challengeUser = function (user, button) {
app.addPopup(UserPopup, {user: user, users: this.info.challenges, sourceEl: button});
};
TournamentBox.prototype.teamSelect = function (team, button) {
app.addPopup(TeamPopup, {team: team, format: this.info.teambuilderFormat, sourceEl: button, room: this.room.id});
};
return TournamentBox;
})();
var UserPopup = this.Popup.extend({
initialize: function (data) {
this.$el.html('');
},
selectUser: function (user) {
this.sourceEl.val(toId(user));
this.sourceEl.parent().parent().find('.tournament-challenge-user').text('vs. ' + user);
this.close();
}
});
var BracketPopup = this.Popup.extend({
type: 'semimodal',
className: 'ps-popup tournament-popout-bracket',
initialize: function (data) {
this.parent = data.parent;
setTimeout(this.updateBracket.bind(this, data.$bracket), 0);
},
updateBracket: function ($bracket) {
this.$el.empty();
this.$el.append($bracket);
this.$el.append('Close
');
// Please keep these two values in-sync with .tournament-popout-bracket in client.css
var maxWidth = 0.8;
var maxHeight = 0.8;
var isWidthOverflowed = $bracket[0].scrollWidth > window.innerWidth * maxWidth;
var isHeightOverflowed = $bracket[0].scrollHeight > window.innerHeight * maxHeight;
this.$el.css('width', isWidthOverflowed ? window.innerWidth * maxWidth : $bracket[0].scrollWidth);
this.$el.css('height', isHeightOverflowed ? window.innerHeight * maxHeight : $bracket[0].scrollHeight);
if (isWidthOverflowed || isHeightOverflowed)
makeDraggable($bracket, null, this.parent ? this.parent.savedPopoutBracketPosition : null);
},
close: function () {
if (this.parent)
this.parent.bracketPopup = null;
Popup.prototype.close.call(this);
}
});
}).call(this, jQuery);