Split battle-log.ts off from battle-dex.ts

This splits battle-dex.ts up into:

- `battle-dex.ts`
  - dex data access, misc tools
- `battle-log.ts`
  - manipulating HTML, especially in battle logs

This turned out to be a pretty significant portion of what was
previously battle-dex.
This commit is contained in:
Guangcong Luo 2018-11-04 13:54:02 -06:00
parent 368ee06bcc
commit 5f05adc856
24 changed files with 732 additions and 728 deletions

View File

@ -4,6 +4,7 @@ node_modules/
/js/battle.js
/js/battledata.js
/js/battle-log.js
/js/battle-dex.js
/js/battle-dex-data.js
/js/battle-animations-moves.js

View File

@ -18,7 +18,7 @@ module.exports = {
"fs": false, "gui": false, "ga": false, "macgap": false, "nwWindow": false, "webkitNotifications": false,
// Battle stuff
"Battle": true, "Pokemon": true, "BattleSound": true, "BattleTooltips": true,
"Battle": true, "Pokemon": true, "BattleSound": true, "BattleTooltips": true, "BattleLog": true,
"BattleAbilities": false, "BattleAliases": false, "BattleBackdrops": false, "BattleBackdropsFive": false, "BattleBackdropsFour": false, "BattleBackdropsThree": false, "BattleEffects": false,
"BattleFormats": false, "BattleFormatsData": false, "BattleLearnsets": false, "BattleItems": false, "BattleMoveAnims": false, "BattleMovedex": false, "BattleNatures": false,
"BattleOtherAnims": false, "BattlePokedex": false,"BattlePokemonSprites": false, "BattlePokemonSpritesBW": false, "BattleSearchCountIndex": false, "BattleSearchIndex": false, "BattleArticleTitles": false,
@ -26,7 +26,7 @@ module.exports = {
// Generic global variables
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Tools": false,
"app": false, "toId": false, "toRoomid": false, "toUserid": false, "toName": false, "getString": false, "hashColor": false, "MD5": false,
"app": false, "toId": false, "toRoomid": false, "toUserid": false, "toName": false, "getString": false, "MD5": false,
"ChatHistory": false, "Topbar": false, "UserList": false,
// Rooms

1
.gitignore vendored
View File

@ -15,6 +15,7 @@ package-lock.json
/js/battle.js
/js/battledata.js
/js/battle-log.js
/js/battle-dex.js
/js/battle-dex-data.js
/js/battle-animations-moves.js

View File

@ -52,7 +52,8 @@ fs.writeFileSync(
'js/battledata.js',
fs.readFileSync('js/battle-dex.js') + '\n\n' +
fs.readFileSync('js/battle-dex-data.js') + '\n\n' +
fs.readFileSync('src/battle-dex-misc.js')
fs.readFileSync('js/battle-log.js') + '\n\n' +
fs.readFileSync('src/battle-log-misc.js')
);
if (!ignoreGraphics) {

View File

@ -99,12 +99,12 @@ var BattleTooltips = (function () {
BattleTooltips.prototype.tooltipAttrs = function (thing, type, ownHeight) {
var roomid = this.room.id;
return ' onclick="BattleTooltips._handleClickFor(event)"' +
' ontouchstart="BattleTooltips._handleTouchStartFor(event, \'' + roomid + '\', \'' + Tools.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' ontouchstart="BattleTooltips._handleTouchStartFor(event, \'' + roomid + '\', \'' + BattleLog.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' ontouchend="BattleTooltips._handleTouchEndFor(event, this)"' +
' ontouchleave="BattleTooltips._handleTouchLeaveFor(event)"' +
' ontouchcancel="BattleTooltips._handleTouchLeaveFor(event)"' +
' onmouseover="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + Tools.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' onfocus="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + Tools.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' onmouseover="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + BattleLog.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' onfocus="BattleTooltips.showTooltipFor(\'' + roomid + '\', \'' + BattleLog.escapeHTML('' + thing, true) + '\',\'' + type + '\', this, ' + (ownHeight ? 'true' : 'false') + ')"' +
' onmouseout="BattleTooltips.hideTooltip()"' +
' onblur="BattleTooltips.hideTooltip()"' +
' onmouseup="BattleTooltips._handleMouseUpFor()"' +

View File

@ -191,7 +191,7 @@
switch (args[0]) {
case 'trapped':
requestData.trapped = true;
var pokeName = pokemon.side.n === 0 ? Tools.escapeHTML(pokemon.name) : "The opposing " + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : Tools.escapeHTML(pokemon.name));
var pokeName = pokemon.side.n === 0 ? BattleLog.escapeHTML(pokemon.name) : "The opposing " + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : BattleLog.escapeHTML(pokemon.name));
this.battle.activityQueue.push('|message|' + pokeName + ' is trapped and cannot switch!');
break;
case 'cant':
@ -554,7 +554,7 @@
} else if (!pokemon || pokemon.fainted) {
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"><span class="picon" style="' + Tools.getPokemonIcon('missingno') + '"></span></button> ';
} else {
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"' + this.tooltips.tooltipAttrs("your" + i, 'pokemon', true) + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : Tools.escapeHTML(pokemon.name)) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
targetMenus[0] += '<button name="chooseMoveTarget" value="' + (i + 1) + '"' + this.tooltips.tooltipAttrs("your" + i, 'pokemon', true) + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + (this.battle.ignoreOpponent || this.battle.ignoreNicks ? pokemon.species : BattleLog.escapeHTML(pokemon.name)) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
for (var i = 0; i < myActive.length; i++) {
@ -573,7 +573,7 @@
} else if (!pokemon || pokemon.fainted) {
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"><span class="picon" style="' + Tools.getPokemonIcon('missingno') + '"></span></button> ';
} else {
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
targetMenus[1] += '<button name="chooseMoveTarget" value="' + (-(i + 1)) + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
@ -587,7 +587,7 @@
} else {
// Move chooser
var hpBar = '<small class="' + (hpRatio < 0.2 ? 'critical' : hpRatio < 0.5 ? 'weak' : 'healthy') + '">HP ' + switchables[pos].hp + '/' + switchables[pos].maxhp + '</small>';
requestTitle += ' What will <strong>' + Tools.escapeHTML(switchables[pos].name) + '</strong> do? ' + hpBar;
requestTitle += ' What will <strong>' + BattleLog.escapeHTML(switchables[pos].name) + '</strong> do? ' + hpBar;
var hasMoves = false;
var moveMenu = '';
@ -605,7 +605,7 @@
if (moveData.disabled) {
movebuttons += '<button disabled="disabled"' + this.tooltips.tooltipAttrs(moveData.move, 'move') + '>';
} else {
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + Tools.escapeHTML(moveData.move) + '" data-target="' + Tools.escapeHTML(moveData.target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'move') + '>';
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(moveData.move) + '" data-target="' + BattleLog.escapeHTML(moveData.target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'move') + '>';
hasMoves = true;
}
movebuttons += name + '<br /><small class="type">' + (moveType ? Tools.getType(moveType).name : "Unknown") + '</small> <small class="pp">' + pp + '</small>&nbsp;</button> ';
@ -620,7 +620,7 @@
var move = Tools.getMove(moveData.move);
var moveType = this.tooltips.getMoveType(move, this.battle.mySide.active[pos] || this.myPokemon[pos]);
if (canZMove[i]) {
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + Tools.escapeHTML(canZMove[i].move) + '" data-target="' + Tools.escapeHTML(canZMove[i].target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'zmove') + '>';
movebuttons += '<button class="type-' + moveType + '" name="chooseMove" value="' + (i + 1) + '" data-move="' + BattleLog.escapeHTML(canZMove[i].move) + '" data-target="' + BattleLog.escapeHTML(canZMove[i].target) + '"' + this.tooltips.tooltipAttrs(moveData.move, 'zmove') + '>';
movebuttons += canZMove[i].move + '<br /><small class="type">' + (moveType ? Tools.getType(moveType).name : "Unknown") + '</small> <small class="pp">1/1</small>&nbsp;</button> ';
} else {
movebuttons += '<button disabled="disabled">&nbsp;</button>';
@ -662,9 +662,9 @@
var pokemon = switchables[i];
pokemon.name = pokemon.ident.substr(4);
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + Tools.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (pokemon.hp ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
} else {
switchMenu += '<button name="chooseSwitch" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
switchMenu += '<button name="chooseSwitch" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
if (this.finalDecisionSwitch && this.battle.gen > 2) {
@ -711,11 +711,11 @@
for (var i = 0; i < myActive.length; i++) {
var pokemon = this.myPokemon[i];
if (pokemon && !pokemon.fainted || this.choice.switchOutFlags[i]) {
controls += '<button disabled' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
controls += '<button disabled' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
} else if (!pokemon) {
controls += '<button disabled></button> ';
} else {
controls += '<button name="chooseSwitchTarget" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
controls += '<button name="chooseSwitchTarget" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') + '</button> ';
}
}
controls += '</div>';
@ -729,18 +729,18 @@
if (this.choice.freedomDegrees >= 1) {
requestTitle += "Choose a Pokémon to send to battle!";
} else {
requestTitle += "Switch <strong>" + Tools.escapeHTML(switchables[pos].name) + "</strong> to:";
requestTitle += "Switch <strong>" + BattleLog.escapeHTML(switchables[pos].name) + "</strong> to:";
}
var switchMenu = '';
for (var i = 0; i < switchables.length; i++) {
var pokemon = switchables[i];
if (pokemon.fainted || i < this.battle.mySide.active.length || this.choice.switchFlags[i]) {
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + Tools.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '>';
switchMenu += '<button class="disabled" name="chooseDisabled" value="' + BattleLog.escapeHTML(pokemon.name) + (pokemon.fainted ? ',fainted' : i < this.battle.mySide.active.length ? ',active' : '') + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '>';
} else {
switchMenu += '<button name="chooseSwitch" value="' + i + '"' + this.tooltips.tooltipAttrs(i, 'sidepokemon') + '>';
}
switchMenu += '<span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
switchMenu += '<span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + (!pokemon.fainted ? '<span class="hpbar' + pokemon.getHPColorClass() + '"><span style="width:' + (Math.round(pokemon.hp * 92 / pokemon.maxhp) || 1) + 'px"></span></span>' + (pokemon.status ? '<span class="status ' + pokemon.status + '"></span>' : '') : '') + '</button> ';
}
var controls = (
@ -774,9 +774,9 @@
var oIndex = this.choice.teamPreview[i] - 1;
var pokemon = switchables[oIndex];
if (i < this.choice.done) {
switchMenu += '<button disabled="disabled"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + '</button> ';
switchMenu += '<button disabled="disabled"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
} else {
switchMenu += '<button name="chooseTeamPreview" value="' + i + '"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + Tools.escapeHTML(pokemon.name) + '</button> ';
switchMenu += '<button name="chooseTeamPreview" value="' + i + '"' + this.tooltips.tooltipAttrs(oIndex, 'sidepokemon') + '><span class="picon" style="' + Tools.getPokemonIcon(pokemon) + '"></span>' + BattleLog.escapeHTML(pokemon.name) + '</button> ';
}
}
@ -987,7 +987,7 @@
filename += '-' + toId(this.battle.p1.name);
filename += '-' + toId(this.battle.p2.name);
e.currentTarget.href = Tools.createReplayFileHref(this);
e.currentTarget.href = BattleLog.createReplayFileHref(this);
e.currentTarget.download = filename + '.html';
e.stopPropagation();

View File

@ -271,7 +271,7 @@
case 'create':
var formatName = window.BattleFormats && BattleFormats[data[0]] ? BattleFormats[data[0]].name : data[0];
var type = data[1];
this.room.$chat.append("<div class=\"notice tournament-message-create\">" + Tools.escapeHTML(formatName) + " " + Tools.escapeHTML(type) + " Tournament created.</div>");
this.room.$chat.append("<div class=\"notice tournament-message-create\">" + BattleLog.escapeHTML(formatName) + " " + BattleLog.escapeHTML(type) + " Tournament created.</div>");
this.room.notifyOnce("Tournament created", "Room: " + this.room.title + "\nFormat: " + formatName + "\nType: " + type, 'tournament-create');
this.curTeamIndex = 0;
this.updateTeams();
@ -317,7 +317,7 @@
break;
case 'disqualify':
this.room.$chat.append("<div class=\"notice tournament-message-disqualify\">" + Tools.escapeHTML(data[0]) + " has been disqualified from the tournament.</div>");
this.room.$chat.append("<div class=\"notice tournament-message-disqualify\">" + BattleLog.escapeHTML(data[0]) + " has been disqualified from the tournament.</div>");
break;
case 'autodq':
@ -448,7 +448,7 @@
case 'battlestart':
var roomid = toRoomid(data[2]).toLowerCase();
this.room.$chat.append('<div class="notice tournament-message-battlestart tournament-' + roomid + '"><a href="' + app.root + roomid + '" class="ilink">' +
"Tournament battle between " + Tools.escapeHTML(data[0]) + " and " + Tools.escapeHTML(data[1]) + " started." +
"Tournament battle between " + BattleLog.escapeHTML(data[0]) + " and " + BattleLog.escapeHTML(data[1]) + " started." +
'</a></div>');
break;
@ -458,7 +458,7 @@
result = "won";
else if (data[2] === 'loss')
result = "lost";
var message = Tools.escapeHTML(data[0]) + " has " + result + " the match " + Tools.escapeHTML(data[3].split(',').join(' - ')) + " against " + Tools.escapeHTML(data[1]) +
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) {
@ -483,9 +483,9 @@
}
var type = endData.generator;
this.room.$chat.append("<div class=\"notice tournament-message-end-winner\">Congratulations to " + Tools.escapeHTML(arrayToPhrase(endData.results[0])) + " for winning the " + Tools.escapeFormat(endData.format) + " " + Tools.escapeHTML(type) + " Tournament!</div>");
this.room.$chat.append("<div class=\"notice tournament-message-end-winner\">Congratulations to " + BattleLog.escapeHTML(arrayToPhrase(endData.results[0])) + " for winning the " + BattleLog.escapeFormat(endData.format) + " " + BattleLog.escapeHTML(type) + " Tournament!</div>");
if (endData.results[1])
this.room.$chat.append("<div class=\"notice tournament-message-end-runnerup\">Runner-up" + (endData.results[1].length > 1 ? "s" : "") + ": " + Tools.escapeHTML(arrayToPhrase(endData.results[1])) + "</div>");
this.room.$chat.append("<div class=\"notice tournament-message-end-runnerup\">Runner-up" + (endData.results[1].length > 1 ? "s" : "") + ": " + BattleLog.escapeHTML(arrayToPhrase(endData.results[1])) + "</div>");
// Fallthrough
@ -510,7 +510,7 @@
case 'error':
var appendError = function (message) {
this.room.$chat.append("<div class=\"notice tournament-message-forceend\">" + Tools.sanitizeHTML(message) + "</div>");
this.room.$chat.append("<div class=\"notice tournament-message-forceend\">" + BattleLog.sanitizeHTML(message) + "</div>");
}.bind(this);
switch (data[0]) {
@ -586,7 +586,7 @@
if (!data.rootNode) {
if (!('users' in data)) return;
var users = data.users.length;
if (users) $div.html('<b>' + users + '</b> user' + (users !== 1 ? 's' : '') + ':<br />' + Tools.escapeHTML(data.users.join(", ")));
if (users) $div.html('<b>' + users + '</b> user' + (users !== 1 ? 's' : '') + ':<br />' + BattleLog.escapeHTML(data.users.join(", ")));
return $div;
}
@ -803,7 +803,7 @@
var UserPopup = this.Popup.extend({
initialize: function (data) {
this.$el.html('<ul class="popupmenu">' + data.users.map(function (user) {
var escapedUser = Tools.escapeHTML(user);
var escapedUser = BattleLog.escapeHTML(user);
return '<li><button name="selectUser" value="' + escapedUser + '"' + (user === data.user ? ' class="sel"' : '') + '>' + escapedUser + '</button></li>';
}).join('') + '</ul>');
},

View File

@ -28,7 +28,7 @@
var name = app.user.get('name');
var userid = app.user.get('userid');
if (this.expired) {
this.$chatAdd.html(this.expired === true ? 'This room is expired' : Tools.sanitizeHTML(this.expired));
this.$chatAdd.html(this.expired === true ? 'This room is expired' : BattleLog.sanitizeHTML(this.expired));
this.$chatbox = null;
} else if (!name) {
this.$chatAdd.html('Connecting...');
@ -37,7 +37,7 @@
this.$chatAdd.html('<form><button name="login">Join chat</button></form>');
this.$chatbox = null;
} else {
this.$chatAdd.html('<form class="chatbox"><label style="' + hashColor(userid) + '">' + Tools.escapeHTML(name) + ':</label> <textarea class="textbox" type="text" size="70" autocomplete="off"></textarea></form>');
this.$chatAdd.html('<form class="chatbox"><label style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name) + ':</label> <textarea class="textbox" type="text" size="70" autocomplete="off"></textarea></form>');
this.$chatbox = this.$chatAdd.find('textarea');
this.$chatbox.autoResize({
animate: false,
@ -479,7 +479,7 @@
if (target === 'extractteams') {
app.addPopup(Popup, {
type: 'modal',
htmlMessage: "Extracted team data:<br /><textarea rows=\"10\" cols=\"60\">" + Tools.escapeHTML(JSON.stringify(Storage.teams)) + "</textarea>"
htmlMessage: "Extracted team data:<br /><textarea rows=\"10\" cols=\"60\">" + BattleLog.escapeHTML(JSON.stringify(Storage.teams)) + "</textarea>"
});
} else {
this.add('|error|Unknown debug command.');
@ -795,7 +795,7 @@
buffer += '<tr>';
} else {
buffer += '<tr class="hidden">';
hiddenFormats.push(Tools.escapeFormat(formatId));
hiddenFormats.push(BattleLog.escapeFormat(formatId));
}
// Validate all the numerical data
@ -804,7 +804,7 @@
if (typeof values[j] !== 'number' && typeof values[j] !== 'string' || isNaN(values[j])) return self.add('|raw|Error: corrupted ranking data');
}
buffer += '<td>' + Tools.escapeFormat(formatId) + '</td><td><strong>' + Math.round(row.elo) + '</strong></td>';
buffer += '<td>' + BattleLog.escapeFormat(formatId) + '</td><td><strong>' + Math.round(row.elo) + '</strong></td>';
if (row.rprd > 100) {
// High rating deviation. Provisional rating.
buffer += '<td>&ndash;</td>';
@ -1231,7 +1231,7 @@
if (!matches) {
return; // bogus room ID could be used to inject JavaScript
}
var format = Tools.escapeFormat(matches[1]);
var format = BattleLog.escapeFormat(matches[1]);
if (silent && !Tools.prefs('showbattles')) return;
@ -1241,7 +1241,7 @@
battletype = format + ' battle';
if (format === 'Random Battle') battletype = 'Random Battle';
}
this.$chat.append('<div class="notice"><a href="' + app.root + id + '" class="ilink">' + battletype + ' started between <strong style="' + hashColor(toUserid(name)) + '">' + Tools.escapeHTML(name) + '</strong> and <strong style="' + hashColor(toUserid(name2)) + '">' + Tools.escapeHTML(name2) + '</strong>.</a></div>');
this.$chat.append('<div class="notice"><a href="' + app.root + id + '" class="ilink">' + battletype + ' started between <strong style="' + BattleLog.hashColor(toUserid(name)) + '">' + BattleLog.escapeHTML(name) + '</strong> and <strong style="' + BattleLog.hashColor(toUserid(name2)) + '">' + BattleLog.escapeHTML(name2) + '</strong>.</a></div>');
break;
case 'j':
@ -1281,7 +1281,7 @@
case 'raw':
case 'html':
this.$chat.append('<div class="notice">' + Tools.sanitizeHTML(row.slice(1).join('|')) + '</div>');
this.$chat.append('<div class="notice">' + BattleLog.sanitizeHTML(row.slice(1).join('|')) + '</div>');
break;
case 'notify':
@ -1307,7 +1307,7 @@
break;
case 'error':
this.$chat.append('<div class="notice message-error">' + Tools.escapeHTML(row.slice(1).join('|')) + '</div>');
this.$chat.append('<div class="notice message-error">' + BattleLog.escapeHTML(row.slice(1).join('|')) + '</div>');
break;
case 'uhtml':
@ -1317,12 +1317,12 @@
if (!html) {
$elements.remove();
} else if (!$elements.length) {
this.$chat.append('<div class="notice uhtml-' + toId(row[1]) + '">' + Tools.sanitizeHTML(html) + '</div>');
this.$chat.append('<div class="notice uhtml-' + toId(row[1]) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
} else if (row[0] === 'uhtmlchange') {
$elements.html(Tools.sanitizeHTML(html));
$elements.html(BattleLog.sanitizeHTML(html));
} else {
$elements.remove();
this.$chat.append('<div class="notice uhtml-' + toId(row[1]) + '">' + Tools.sanitizeHTML(html) + '</div>');
this.$chat.append('<div class="notice uhtml-' + toId(row[1]) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
}
break;
@ -1349,7 +1349,7 @@
case 'tournaments':
if (Tools.prefs('notournaments')) {
if (row[1] === 'create') {
this.$chat.append('<div class="notice">' + Tools.escapeFormat(row[2]) + ' ' + Tools.escapeHTML(row[3]) + ' tournament created (and hidden because you have tournaments disabled).</div>');
this.$chat.append('<div class="notice">' + BattleLog.escapeFormat(row[2]) + ' ' + BattleLog.escapeHTML(row[3]) + ' tournament created (and hidden because you have tournaments disabled).</div>');
} else if (row[1] === 'start') {
this.$chat.append('<div class="notice">Tournament started.</div>');
} else if (row[1] === 'forceend') {
@ -1364,11 +1364,11 @@
// fallthrough in case of unparsed message
case '':
this.$chat.append('<div class="notice">' + Tools.escapeHTML(row.slice(1).join('|')) + '</div>');
this.$chat.append('<div class="notice">' + BattleLog.escapeHTML(row.slice(1).join('|')) + '</div>');
break;
default:
this.$chat.append('<div class="notice"><code>|' + Tools.escapeHTML(row.join('|')) + '</code></div>');
this.$chat.append('<div class="notice"><code>|' + BattleLog.escapeHTML(row.join('|')) + '</code></div>');
break;
}
}
@ -1465,7 +1465,7 @@
message += ', ';
}
}
message += Tools.escapeHTML(list[j]);
message += BattleLog.escapeHTML(list[j]);
}
message += ' joined';
}
@ -1494,7 +1494,7 @@
message += ', ';
}
}
message += Tools.escapeHTML(list[j]);
message += BattleLog.escapeHTML(list[j]);
}
message += ' left<br />';
}
@ -1519,11 +1519,11 @@
if (pm) {
var pmuserid = toUserid(pm);
var oName = pmuserid === app.user.get('userid') ? name : pm;
var clickableName = '<span class="username" data-name="' + Tools.escapeHTML(name) + '">' + Tools.escapeHTML(name.substr(1)) + '</span>';
var clickableName = '<span class="username" data-name="' + BattleLog.escapeHTML(name) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</span>';
this.$chat.append(
'<div class="chat chatmessage-' + toId(name) + '">' + ChatRoom.getTimestamp('lobby', msgTime) +
'<strong style="' + hashColor(userid) + '">' + clickableName + ':</strong>' +
'<span class="message-pm"><i class="pmnote" data-name="' + Tools.escapeHTML(oName) + '">(Private to ' + Tools.escapeHTML(pm) + ')</i> ' + Tools.parseMessage(message) + '</span>' +
'<strong style="' + BattleLog.hashColor(userid) + '">' + clickableName + ':</strong>' +
'<span class="message-pm"><i class="pmnote" data-name="' + BattleLog.escapeHTML(oName) + '">(Private to ' + BattleLog.escapeHTML(pm) + ')</i> ' + BattleLog.parseMessage(message) + '</span>' +
'</div>'
);
return; // PMs independently notify in the main menu; no need to make them notify again with `inchatpm`.
@ -1546,7 +1546,7 @@
}
var isHighlighted = userid !== app.user.get('userid') && this.getHighlight(message);
var parsedMessage = Tools.parseChatMessage(message, name, ChatRoom.getTimestamp('chat', msgTime), isHighlighted);
var parsedMessage = BattleLog.parseChatMessage(message, name, ChatRoom.getTimestamp('chat', msgTime), isHighlighted);
if (!$.isArray(parsedMessage)) parsedMessage = [parsedMessage];
for (var i = 0; i < parsedMessage.length; i++) {
if (!parsedMessage[i]) continue;
@ -1687,17 +1687,17 @@
var text = '';
// Sanitising the `userid` here is probably unnecessary, because
// IDs can't contain anything dangerous.
text += '<li' + (this.room.userForm === userid ? ' class="cur"' : '') + ' id="' + this.room.id + '-userlist-user-' + Tools.escapeHTML(userid) + '">';
text += '<button class="userbutton username" data-name="' + Tools.escapeHTML(name) + '">';
text += '<li' + (this.room.userForm === userid ? ' class="cur"' : '') + ' id="' + this.room.id + '-userlist-user-' + BattleLog.escapeHTML(userid) + '">';
text += '<button class="userbutton username" data-name="' + BattleLog.escapeHTML(name) + '">';
var group = name.charAt(0);
var details = Config.groups[group] || {type: 'user'};
text += '<em class="group' + (details.group === 2 ? ' staffgroup' : '') + '">' + Tools.escapeHTML(group) + '</em>';
text += '<em class="group' + (details.group === 2 ? ' staffgroup' : '') + '">' + BattleLog.escapeHTML(group) + '</em>';
if (details.type === 'leadership') {
text += '<strong><em style="' + hashColor(userid) + '">' + Tools.escapeHTML(name.substr(1)) + '</em></strong>';
text += '<strong><em style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</em></strong>';
} else if (details.type === 'staff') {
text += '<strong style="' + hashColor(userid) + '">' + Tools.escapeHTML(name.substr(1)) + '</strong>';
text += '<strong style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</strong>';
} else {
text += '<span style="' + hashColor(userid) + '">' + Tools.escapeHTML(name.substr(1)) + '</span>';
text += '<span style="' + BattleLog.hashColor(userid) + '">' + BattleLog.escapeHTML(name.substr(1)) + '</span>';
}
text += '</button>';
text += '</li>';

View File

@ -48,14 +48,14 @@
break;
case 'pagehtml':
this.$el.html(Tools.sanitizeHTML(row[1]));
this.$el.html(BattleLog.sanitizeHTML(row[1]));
this.subtleNotifyOnce();
break;
case 'selectorhtml':
var pipeIndex2 = row[1].indexOf('|');
if (pipeIndex2 < 0) return;
this.$(row[1].slice(0, pipeIndex2)).html(Tools.sanitizeHTML(row[1].slice(pipeIndex2 + 1)));
this.$(row[1].slice(0, pipeIndex2)).html(BattleLog.sanitizeHTML(row[1].slice(pipeIndex2 + 1)));
this.subtleNotifyOnce();
break;
@ -96,7 +96,7 @@
return;
}
if (this.curFormat !== data[0]) return;
buf += Tools.sanitizeHTML(data[1]) + '</div>';
buf += BattleLog.sanitizeHTML(data[1]) + '</div>';
this.$el.html(buf);
}, this);
},
@ -119,9 +119,9 @@
if (!format.rated || !format.searchShow) continue;
if (format.section && format.section !== curSection) {
curSection = format.section;
buf += '</ul><h3>' + Tools.escapeHTML(curSection) + '</h3><ul style="list-style:none;margin:0;padding:0">';
buf += '</ul><h3>' + BattleLog.escapeHTML(curSection) + '</h3><ul style="list-style:none;margin:0;padding:0">';
}
buf += '<li style="margin:5px"><button name="selectFormat" value="' + i + '" class="button" style="width:320px;height:30px;text-align:left;font:12pt Verdana">' + Tools.escapeFormat(format.id) + '</button></li>';
buf += '<li style="margin:5px"><button name="selectFormat" value="' + i + '" class="button" style="width:320px;height:30px;text-align:left;font:12pt Verdana">' + BattleLog.escapeFormat(format.id) + '</button></li>';
}
buf += '</ul></div>';
this.$el.html(buf);
@ -141,7 +141,7 @@
}, function (data) {
if (self.curFormat !== format) return;
var buf = '<div class="ladder pad"><p><button name="selectFormat"><i class="fa fa-chevron-left"></i> Format List</button></p><p><button class="button" name="refresh"><i class="fa fa-refresh"></i> Refresh</button></p>';
buf += '<h3>' + Tools.escapeFormat(format) + ' Top 500</h3>';
buf += '<h3>' + BattleLog.escapeFormat(format) + ' Top 500</h3>';
buf += data + '</div>';
self.$el.html(buf);
}, 'html');

View File

@ -143,7 +143,7 @@
var autoscroll = ($chatFrame.scrollTop() + 60 >= $chat.height() - $chatFrame.height());
var parsedMessage = Tools.parseChatMessage(message, name, ChatRoom.getTimestamp('pms'), false, $chat);
var parsedMessage = BattleLog.parseChatMessage(message, name, ChatRoom.getTimestamp('pms'), false, $chat);
if (!$.isArray(parsedMessage)) parsedMessage = [parsedMessage];
for (var i = 0; i < parsedMessage.length; i++) {
if (!parsedMessage[i]) continue;
@ -176,12 +176,12 @@
if (group === ' ') {
group = '';
} else {
group = '<small>' + Tools.escapeHTML(group) + '</small>';
group = '<small>' + BattleLog.escapeHTML(group) + '</small>';
}
var buf = '<div class="pm-window pm-window-' + userid + '" data-userid="' + userid + '" data-name="' + name + '">';
buf += '<h3><button class="closebutton" href="' + app.root + 'teambuilder" tabindex="-1" aria-label="Close"><i class="fa fa-times-circle"></i></button>';
buf += '<button class="minimizebutton" href="' + app.root + 'teambuilder" tabindex="-1" aria-label="Minimize"><i class="fa fa-minus-circle"></i></button>';
buf += group + Tools.escapeHTML(name.substr(1)) + '</h3>';
buf += group + BattleLog.escapeHTML(name.substr(1)) + '</h3>';
buf += '<div class="pm-log"><div class="inner" role="log"></div></div>';
buf += '<div class="pm-log-add"><form class="chatbox nolabel"><textarea class="textbox" type="text" size="70" autocomplete="off" name="message"></textarea></form></div></div>';
$pmWindow = $(buf).prependTo(this.$pmBox);
@ -569,7 +569,7 @@
for (var roomid in this.games) {
var name = this.games[roomid];
if (name.slice(-1) === '*') name = name.slice(0, -1);
buf += '<div><a href="/' + toRoomid(roomid) + '" class="ilink" style="text-align: center">' + Tools.escapeHTML(name) + '</a></div>';
buf += '<div><a href="/' + toRoomid(roomid) + '" class="ilink" style="text-align: center">' + BattleLog.escapeHTML(name) + '</a></div>';
}
buf += '</div>';
if (!$searchGroup.is(':visible')) buf += '<p class="buttonbar"><button name="showSearchGroup">Add game</button></p>';
@ -606,7 +606,7 @@
var userid = toId(name);
var $challenge = this.openChallenge(name);
var buf = '<form class="battleform"><p>Waiting for ' + Tools.escapeHTML(name) + '...</p>';
var buf = '<form class="battleform"><p>Waiting for ' + BattleLog.escapeHTML(name) + '...</p>';
buf += '<p><label class="label">Format:</label>' + this.renderFormats(challenge.format, true) + '</p>';
buf += '<p class="buttonbar"><button name="cancelChallenge">Cancel</button></p></form>';
@ -623,10 +623,10 @@
if (data.challengesFrom[userid]) {
var format = data.challengesFrom[userid];
if (!$pmWindow.find('.challenge').length) {
self.notifyOnce("Challenge from " + name, "Format: " + Tools.escapeFormat(format), 'challenge:' + userid);
self.notifyOnce("Challenge from " + name, "Format: " + BattleLog.escapeFormat(format), 'challenge:' + userid);
}
var $challenge = self.openChallenge(name, $pmWindow);
var buf = '<form class="battleform"><p>' + Tools.escapeHTML(name) + ' wants to battle!</p>';
var buf = '<form class="battleform"><p>' + BattleLog.escapeHTML(name) + ' wants to battle!</p>';
buf += '<p><label class="label">Format:</label>' + self.renderFormats(format, true) + '</p>';
buf += '<p><label class="label">Team:</label>' + self.renderTeams(format) + '</p>';
buf += '<p class="buttonbar"><button name="acceptChallenge"><strong>Accept</strong></button> <button name="rejectChallenge">Reject</button></p></form>';
@ -729,7 +729,7 @@
}
$challenge = this.openChallenge(name);
var buf = '<form class="battleform"><p>Challenge ' + Tools.escapeHTML(name) + '?</p>';
var buf = '<form class="battleform"><p>Challenge ' + BattleLog.escapeHTML(name) + '?</p>';
buf += '<p><label class="label">Format:</label>' + this.renderFormats(format) + '</p>';
buf += '<p><label class="label">Team:</label>' + this.renderTeams(format, teamIndex) + '</p>';
buf += '<p class="buttonbar"><button name="makeChallenge"><strong>Challenge</strong></button> <button name="dismissChallenge">Cancel</button></p></form>';
@ -773,7 +773,7 @@
return;
}
var buf = '<form class="battleform pending"><p>Challenging ' + Tools.escapeHTML(name) + '...</p>';
var buf = '<form class="battleform pending"><p>Challenging ' + BattleLog.escapeHTML(name) + '...</p>';
buf += '<p><label class="label">Format:</label>' + this.renderFormats(format, true) + '</p>';
buf += '<p class="buttonbar"><button name="cancelChallenge">Cancel</button></p></form>';
@ -802,7 +802,7 @@
curFormat: '',
renderFormats: function (formatid, noChoice) {
if (!window.BattleFormats) {
return '<button class="select formatselect" name="format" disabled value="' + Tools.escapeHTML(formatid) + '"><em>Loading...</em></button>';
return '<button class="select formatselect" name="format" disabled value="' + BattleLog.escapeHTML(formatid) + '"><em>Loading...</em></button>';
}
if (_.isEmpty(BattleFormats)) {
return '<button class="select formatselect" name="format" disabled><em>No formats available</em></button>';
@ -820,7 +820,7 @@
}
formatid = this.curFormat;
}
return '<button class="select formatselect' + (noChoice ? ' preselected' : '') + '" name="format" value="' + formatid + '"' + (noChoice ? ' disabled' : '') + '>' + Tools.escapeFormat(formatid) + '</button>';
return '<button class="select formatselect' + (noChoice ? ' preselected' : '') + '" name="format" value="' + formatid + '"' + (noChoice ? ' disabled' : '') + '>' + BattleLog.escapeFormat(formatid) + '</button>';
},
curTeamFormat: '',
curTeamIndex: 0,
@ -957,9 +957,9 @@
if (!bufs[curBuf]) {
bufs[curBuf] = '';
}
bufs[curBuf] += '<li><h3>' + Tools.escapeHTML(curSection) + '</li>';
bufs[curBuf] += '<li><h3>' + BattleLog.escapeHTML(curSection) + '</li>';
}
var formatName = Tools.escapeFormat(format.id);
var formatName = BattleLog.escapeFormat(format.id);
if (formatName.charAt(0) !== '[') formatName = '[Gen 6] ' + formatName;
formatName = formatName.replace('[Gen 7] ', '');
bufs[curBuf] += '<li><button name="selectFormat" value="' + i + '"' + (curFormat === i ? ' class="sel"' : '') + '>' + formatName + '</button></li>';
@ -989,7 +989,7 @@
var $teamButton = this.sourceEl.closest('form').find('button[name=team]');
if ($teamButton.length) $teamButton.replaceWith(app.rooms[''].renderTeams(format));
}
this.sourceEl.val(format).html(Tools.escapeFormat(format) || '(Select a format)');
this.sourceEl.val(format).html(BattleLog.escapeFormat(format) || '(Select a format)');
this.close();
}
@ -1023,22 +1023,22 @@
if (!teams.length) {
bufs[curBuf] = '<li><p><em>You have no teams</em></p></li>';
bufs[curBuf] += '<li><button name="teambuilder" class="button"><strong>Teambuilder</strong><br />' + Tools.escapeFormat(teamFormat) + ' teams</button></li>';
bufs[curBuf] += '<li><button name="teambuilder" class="button"><strong>Teambuilder</strong><br />' + BattleLog.escapeFormat(teamFormat) + ' teams</button></li>';
} else {
var curTeam = (data.team === '' ? -1 : +data.team);
var count = 0;
if (teamFormat) {
bufs[curBuf] = '<li><h3>' + Tools.escapeFormat(teamFormat) + ' teams</h3></li>';
bufs[curBuf] = '<li><h3>' + BattleLog.escapeFormat(teamFormat) + ' teams</h3></li>';
for (var i = 0; i < teams.length; i++) {
if ((!teams[i].format && !teamFormat) || teams[i].format === teamFormat) {
var selected = (i === curTeam);
bufs[curBuf] += '<li><button name="selectTeam" value="' + i + '"' + (selected ? ' class="sel"' : '') + '>' + Tools.escapeHTML(teams[i].name) + '</button></li>';
bufs[curBuf] += '<li><button name="selectTeam" value="' + i + '"' + (selected ? ' class="sel"' : '') + '>' + BattleLog.escapeHTML(teams[i].name) + '</button></li>';
count++;
if (count % bufBoundary == 0 && curBuf < 4) curBuf++;
}
}
if (!count) bufs[curBuf] += '<li><p><em>You have no ' + Tools.escapeFormat(teamFormat) + ' teams</em></p></li>';
bufs[curBuf] += '<li><button name="teambuilder" class="button"><strong>Teambuilder</strong><br />' + Tools.escapeFormat(teamFormat) + ' teams</button></li>';
if (!count) bufs[curBuf] += '<li><p><em>You have no ' + BattleLog.escapeFormat(teamFormat) + ' teams</em></p></li>';
bufs[curBuf] += '<li><button name="teambuilder" class="button"><strong>Teambuilder</strong><br />' + BattleLog.escapeFormat(teamFormat) + ' teams</button></li>';
bufs[curBuf] += '<li><h3>Other teams</h3></li>';
} else {
bufs[curBuf] = '<li><button name="teambuilder" class="button"><strong>Teambuilder</strong></button></li>';
@ -1049,7 +1049,7 @@
for (var i = 0; i < teams.length; i++) {
if (teamFormat && teams[i].format === teamFormat) continue;
var selected = (i === curTeam);
bufs[curBuf] += '<li><button name="selectTeam" value="' + i + '"' + (selected ? ' class="sel"' : '') + '>' + Tools.escapeHTML(teams[i].name) + '</button></li>';
bufs[curBuf] += '<li><button name="selectTeam" value="' + i + '"' + (selected ? ' class="sel"' : '') + '>' + BattleLog.escapeHTML(teams[i].name) + '</button></li>';
count++;
if (count % bufBoundary == 0 && curBuf < 4) curBuf++;
}
@ -1112,7 +1112,7 @@
}
var team = Storage.teams[i];
if (!team) return 'Error: Corrupted team';
var buf = '<strong>' + Tools.escapeHTML(team.name) + '</strong><small>';
var buf = '<strong>' + BattleLog.escapeHTML(team.name) + '</strong><small>';
buf += Storage.getTeamIcons(team) + '</small>';
return buf;
}

View File

@ -59,12 +59,12 @@
},
renderRoomBtn: function (roomData) {
var id = toId(roomData.title);
var buf = '<div><a href="' + app.root + id + '" class="ilink"><small style="float:right">(' + Number(roomData.userCount) + ' users)</small><strong><i class="fa fa-comment-o"></i> ' + Tools.escapeHTML(roomData.title) + '<br /></strong><small>' + Tools.escapeHTML(roomData.desc || '');
var buf = '<div><a href="' + app.root + id + '" class="ilink"><small style="float:right">(' + Number(roomData.userCount) + ' users)</small><strong><i class="fa fa-comment-o"></i> ' + BattleLog.escapeHTML(roomData.title) + '<br /></strong><small>' + BattleLog.escapeHTML(roomData.desc || '');
if (roomData.subRooms && roomData.subRooms.length) {
buf += '<br/><i class="fa fa-level-up fa-rotate-90"></i> Subrooms: <strong>';
for (var i = 0; i < roomData.subRooms.length; i++) {
if (i) buf += ', ';
buf += '<i class="fa fa-comment-o"></i> ' + Tools.escapeHTML(roomData.subRooms[i]);
buf += '<i class="fa fa-comment-o"></i> ' + BattleLog.escapeHTML(roomData.subRooms[i]);
}
buf += '</strong>';
}
@ -167,14 +167,14 @@
renderRoomBtn: function (id, roomData, matches) {
var format = (matches[1] || '');
var formatBuf = '';
if (roomData.minElo) formatBuf += '<small style="float:right">(' + (typeof roomData.minElo === 'number' ? 'rated: ' : '') + Tools.escapeHTML(roomData.minElo) + ')</small>';
formatBuf += (format ? '<small>[' + Tools.escapeFormat(format) + ']</small><br />' : '');
var roomDesc = formatBuf + '<em class="p1">' + Tools.escapeHTML(roomData.p1) + '</em> <small class="vs">vs.</small> <em class="p2">' + Tools.escapeHTML(roomData.p2) + '</em>';
if (roomData.minElo) formatBuf += '<small style="float:right">(' + (typeof roomData.minElo === 'number' ? 'rated: ' : '') + BattleLog.escapeHTML(roomData.minElo) + ')</small>';
formatBuf += (format ? '<small>[' + BattleLog.escapeFormat(format) + ']</small><br />' : '');
var roomDesc = formatBuf + '<em class="p1">' + BattleLog.escapeHTML(roomData.p1) + '</em> <small class="vs">vs.</small> <em class="p2">' + BattleLog.escapeHTML(roomData.p2) + '</em>';
if (!roomData.p1) {
matches = id.match(/[^0-9]([0-9]*)$/);
roomDesc = formatBuf + 'empty room ' + matches[1];
} else if (!roomData.p2) {
roomDesc = formatBuf + '<em class="p1">' + Tools.escapeHTML(roomData.p1) + '</em>';
roomDesc = formatBuf + '<em class="p1">' + BattleLog.escapeHTML(roomData.p1) + '</em>';
}
return '<div><a href="' + app.root + id + '" class="ilink">' + roomDesc + '</a></div>';
},
@ -204,8 +204,8 @@
buf.push(this.renderRoomBtn(id, roomData, matches));
}
if (!buf.length) return this.$list.html('<p>No ' + Tools.escapeFormat(this.format) + ' battles are going on right now.</p>');
return this.$list.html('<p>' + buf.length + (buf.length === 100 ? '+' : '') + ' ' + Tools.escapeFormat(this.format) + ' ' + (buf.length === 1 ? 'battle' : 'battles') + '</p>' + buf.join(""));
if (!buf.length) return this.$list.html('<p>No ' + BattleLog.escapeFormat(this.format) + ' battles are going on right now.</p>');
return this.$list.html('<p>' + buf.length + (buf.length === 100 ? '+' : '') + ' ' + BattleLog.escapeFormat(this.format) + ' ' + (buf.length === 1 ? 'battle' : 'battles') + '</p>' + buf.join(""));
},
refresh: function () {
var elofilter = '';

View File

@ -149,14 +149,14 @@
if (this.exportMode) {
if (this.curFolder) {
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button></div>';
buf += '<div class="teamedit"><textarea readonly class="textbox" rows="17">' + Tools.escapeHTML(Storage.exportFolder(this.curFolder)) + '</textarea></div>';
buf += '<div class="teamedit"><textarea readonly class="textbox" rows="17">' + BattleLog.escapeHTML(Storage.exportFolder(this.curFolder)) + '</textarea></div>';
} else {
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button> <button name="saveBackup" class="savebutton"><i class="fa fa-floppy-o"></i> Save</button></div>';
buf += '<div class="teamedit"><textarea class="textbox" rows="17">';
if (Storage.teams.length > 350) {
buf += Tools.escapeHTML(Storage.getPackedTeams());
buf += BattleLog.escapeHTML(Storage.getPackedTeams());
} else {
buf += Tools.escapeHTML(Storage.exportAllTeams());
buf += BattleLog.escapeHTML(Storage.exportAllTeams());
}
buf += '</textarea></div>';
}
@ -291,7 +291,7 @@
formatName = '(uncategorized)';
format = '/';
} else {
formatName = Tools.escapeHTML(formatName);
formatName = BattleLog.escapeHTML(formatName);
}
buf += '<div class="folder' + (this.curFolder === format ? ' cur"><div class="folderhack3"><div class="folderhack1"></div><div class="folderhack2"></div>' : '">') + '<div class="selectFolder" data-value="' + format + '"><i class="fa ' + (this.curFolder === format ? 'fa-folder-open' : 'fa-folder') + (format === '/' ? '-o' : '') + '"></i>' + formatName + '</div></div>' + (this.curFolder === format ? '</div>' : '');
continue;
@ -347,7 +347,7 @@
var newButtonText = "New Team";
if (filterFolder) newButtonText = "New Team in folder";
if (filterFormat && filterFormat !== 'gen7') {
newButtonText = "New " + Tools.escapeFormat(filterFormat) + " Team";
newButtonText = "New " + BattleLog.escapeFormat(filterFormat) + " Team";
}
buf += '<p><button name="newTop" class="button big"><i class="fa fa-plus-circle"></i> ' + newButtonText + '</button></p>';
@ -399,7 +399,7 @@
// teams are <div>s rather than <button>s because Firefox doesn't
// support dragging and dropping buttons.
buf += '<li><div name="edit" data-value="' + i + '" class="team" draggable="true">' + formatText + '<strong>' + Tools.escapeHTML(team.name) + '</strong><br /><small>';
buf += '<li><div name="edit" data-value="' + i + '" class="team" draggable="true">' + formatText + '<strong>' + BattleLog.escapeHTML(team.name) + '</strong><br /><small>';
buf += Storage.getTeamIcons(team);
buf += '</small></div><button name="edit" value="' + i + '"><i class="fa fa-pencil" aria-label="Edit" title="Edit (you can also just click on the team)"></i></button><button name="newTop" value="' + i + '" title="Duplicate" aria-label="Duplicate"><i class="fa fa-clone"></i></button><button name="delete" value="' + i + '"><i class="fa fa-trash"></i> Delete</button></li>';
}
@ -1019,10 +1019,10 @@
var buf = '';
if (this.exportMode) {
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button> <input class="textbox teamnameedit" type="text" class="teamnameedit" size="30" value="' + Tools.escapeHTML(this.curTeam.name) + '" /> <button name="saveImport"><i class="fa fa-upload"></i> Import/Export</button> <button name="saveImport" class="savebutton"><i class="fa fa-floppy-o"></i> Save</button></div>';
buf += '<div class="teamedit"><textarea class="textbox" rows="17">' + Tools.escapeHTML(Storage.exportTeam(this.curSetList)) + '</textarea></div>';
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button> <input class="textbox teamnameedit" type="text" class="teamnameedit" size="30" value="' + BattleLog.escapeHTML(this.curTeam.name) + '" /> <button name="saveImport"><i class="fa fa-upload"></i> Import/Export</button> <button name="saveImport" class="savebutton"><i class="fa fa-floppy-o"></i> Save</button></div>';
buf += '<div class="teamedit"><textarea class="textbox" rows="17">' + BattleLog.escapeHTML(Storage.exportTeam(this.curSetList)) + '</textarea></div>';
} else {
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button> <input class="textbox teamnameedit" type="text" class="teamnameedit" size="30" value="' + Tools.escapeHTML(this.curTeam.name) + '" /> <button name="import"><i class="fa fa-upload"></i> Import/Export</button></div>';
buf = '<div class="pad"><button name="back"><i class="fa fa-chevron-left"></i> List</button> <input class="textbox teamnameedit" type="text" class="teamnameedit" size="30" value="' + BattleLog.escapeHTML(this.curTeam.name) + '" /> <button name="import"><i class="fa fa-upload"></i> Import/Export</button></div>';
buf += '<div class="teamchartbox">';
buf += '<ol class="teamchart">';
buf += '<li>' + this.clipboardHTML() + '</li>';
@ -1038,7 +1038,7 @@
};
if (exports.BattleFormats) {
buf += '<li class="format-select">';
buf += '<label class="label">Format:</label><button class="select formatselect teambuilderformatselect" name="format" value="' + this.curTeam.format + '">' + (isGenericFormat(this.curTeam.format) ? '<em>Select a format</em>' : Tools.escapeFormat(this.curTeam.format)) + '</button>';
buf += '<label class="label">Format:</label><button class="select formatselect teambuilderformatselect" name="format" value="' + this.curTeam.format + '">' + (isGenericFormat(this.curTeam.format) ? '<em>Select a format</em>' : BattleLog.escapeFormat(this.curTeam.format)) + '</button>';
var btnClass = 'button' + (!this.curSetList.length ? ' disabled' : '');
buf += ' <button name="validate" class="' + btnClass + '"><i class="fa fa-check"></i> Validate</button></li>';
}
@ -1081,7 +1081,7 @@
}
buf += '<div class="setmenu"><button name="copySet"><i class="fa fa-files-o"></i>Copy</button> <button name="importSet"><i class="fa fa-upload"></i>Import/Export</button> <button name="moveSet"><i class="fa fa-arrows"></i>Move</button> <button name="deleteSet"><i class="fa fa-trash"></i>Delete</button></div>';
buf += '<div class="setchart-nickname">';
buf += '<label>Nickname</label><input type="text" name="nickname" class="textbox" value="' + Tools.escapeHTML(set.name || '') + '" placeholder="' + Tools.escapeHTML(template.baseSpecies) + '" />';
buf += '<label>Nickname</label><input type="text" name="nickname" class="textbox" value="' + BattleLog.escapeHTML(set.name || '') + '" placeholder="' + BattleLog.escapeHTML(template.baseSpecies) + '" />';
buf += '</div>';
buf += '<div class="setchart" style="' + Tools.getTeambuilderSprite(set, this.curTeam.gen) + ';">';
@ -1092,7 +1092,7 @@
} else {
buf += '<div class="setcell-sprite"></div>';
}
buf += '<div class="setcell setcell-pokemon"><label>Pok&eacute;mon</label><input type="text" name="pokemon" class="textbox chartinput" value="' + Tools.escapeHTML(set.species) + '" /></div></div>';
buf += '<div class="setcell setcell-pokemon"><label>Pok&eacute;mon</label><input type="text" name="pokemon" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.species) + '" /></div></div>';
// details
buf += '<div class="setcol setcol-details"><div class="setrow">';
@ -1131,17 +1131,17 @@
buf += '</div></div>';
buf += '<div class="setrow">';
if (this.curTeam.gen > 1) buf += '<div class="setcell setcell-item"><label>Item</label><input type="text" name="item" class="textbox chartinput" value="' + Tools.escapeHTML(set.item) + '" /></div>';
if (this.curTeam.gen > 2) buf += '<div class="setcell setcell-ability"><label>Ability</label><input type="text" name="ability" class="textbox chartinput" value="' + Tools.escapeHTML(set.ability) + '" /></div>';
if (this.curTeam.gen > 1) buf += '<div class="setcell setcell-item"><label>Item</label><input type="text" name="item" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.item) + '" /></div>';
if (this.curTeam.gen > 2) buf += '<div class="setcell setcell-ability"><label>Ability</label><input type="text" name="ability" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.ability) + '" /></div>';
buf += '</div></div>';
// moves
if (!set.moves) set.moves = [];
buf += '<div class="setcol setcol-moves"><div class="setcell"><label>Moves</label>';
buf += '<input type="text" name="move1" class="textbox chartinput" value="' + Tools.escapeHTML(set.moves[0]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move2" class="textbox chartinput" value="' + Tools.escapeHTML(set.moves[1]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move3" class="textbox chartinput" value="' + Tools.escapeHTML(set.moves[2]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move4" class="textbox chartinput" value="' + Tools.escapeHTML(set.moves[3]) + '" /></div>';
buf += '<input type="text" name="move1" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.moves[0]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move2" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.moves[1]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move3" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.moves[2]) + '" /></div>';
buf += '<div class="setcell"><input type="text" name="move4" class="textbox chartinput" value="' + BattleLog.escapeHTML(set.moves[3]) + '" /></div>';
buf += '</div>';
// stats
@ -1533,9 +1533,9 @@
buf += '<button disabled="disabled" class="addpokemon" aria-label="Add Pok&eacute;mon"><i class="fa fa-plus"></i></button> ';
isAdd = true;
} else if (i == this.curSetLoc) {
buf += '<button disabled="disabled" class="pokemon">' + pokemonicon + Tools.escapeHTML(set.name || Tools.getTemplate(set.species).baseSpecies || '<i class="fa fa-plus"></i>') + '</button> ';
buf += '<button disabled="disabled" class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || Tools.getTemplate(set.species).baseSpecies || '<i class="fa fa-plus"></i>') + '</button> ';
} else {
buf += '<button name="selectPokemon" value="' + i + '" class="pokemon">' + pokemonicon + Tools.escapeHTML(set.name || Tools.getTemplate(set.species).baseSpecies) + '</button> ';
buf += '<button name="selectPokemon" value="' + i + '" class="pokemon">' + pokemonicon + BattleLog.escapeHTML(set.name || Tools.getTemplate(set.species).baseSpecies) + '</button> ';
}
}
if (this.curSetList.length < 6 && !isAdd) {
@ -1902,7 +1902,7 @@
buf += '<div class="col evslidercol"><div></div>';
for (var i in stats) {
if (i === 'spd' && this.curTeam.gen === 1) continue;
buf += '<div><input type="range" name="evslider-' + i + '" value="' + Tools.escapeHTML(set.evs[i] === undefined ? (this.curTeam.gen > 2 ? '0' : '252') : '' + set.evs[i]) + '" min="0" max="252" step="4" class="evslider" tabindex="-1" aria-hidden="true" /></div>';
buf += '<div><input type="range" name="evslider-' + i + '" value="' + BattleLog.escapeHTML(set.evs[i] === undefined ? (this.curTeam.gen > 2 ? '0' : '252') : '' + set.evs[i]) + '" min="0" max="252" step="4" class="evslider" tabindex="-1" aria-hidden="true" /></div>';
}
buf += '</div>';
@ -1912,7 +1912,7 @@
for (var i in stats) {
if (set.ivs[i] === undefined || isNaN(set.ivs[i])) set.ivs[i] = 31;
var val = '' + (set.ivs[i]);
buf += '<div><input type="number" name="iv-' + i + '" value="' + Tools.escapeHTML(val) + '" class="textbox inputform numform" min="0" max="31" step="1" /></div>';
buf += '<div><input type="number" name="iv-' + i + '" value="' + BattleLog.escapeHTML(val) + '" class="textbox inputform numform" min="0" max="31" step="1" /></div>';
}
var hpType = '';
if (set.moves) {
@ -2032,7 +2032,7 @@
for (var i in stats) {
if (set.ivs[i] === undefined || isNaN(set.ivs[i])) set.ivs[i] = 31;
var val = '' + Math.floor(set.ivs[i] / 2);
buf += '<div><input type="number" name="iv-' + i + '" value="' + Tools.escapeHTML(val) + '" class="textbox inputform numform" min="0" max="15" step="1" /></div>';
buf += '<div><input type="number" name="iv-' + i + '" value="' + BattleLog.escapeHTML(val) + '" class="textbox inputform numform" min="0" max="15" step="1" /></div>';
}
buf += '</div>';
}
@ -2304,7 +2304,7 @@
buf += '<div class="resultheader"><h3>Details</h3></div>';
buf += '<form class="detailsform">';
buf += '<div class="formrow"><label class="formlabel">Level:</label><div><input type="number" min="1" max="100" step="1" name="level" value="' + Tools.escapeHTML(set.level || 100) + '" class="textbox inputform numform" /></div></div>';
buf += '<div class="formrow"><label class="formlabel">Level:</label><div><input type="number" min="1" max="100" step="1" name="level" value="' + BattleLog.escapeHTML(set.level || 100) + '" class="textbox inputform numform" /></div></div>';
if (this.curTeam.gen > 1) {
buf += '<div class="formrow"><label class="formlabel">Gender:</label><div>';
@ -3354,7 +3354,7 @@
if (i !== data.i && i !== data.i + 1) {
buf += '<li><button name="moveHere" value="' + i + '"><i class="fa fa-arrow-right"></i> Move here</button></li>';
}
buf += '<li' + (i === data.i ? ' style="opacity:.3"' : ' style="opacity:.6"') + '><span class="picon" style="display:inline-block;vertical-align:middle;' + Tools.getPokemonIcon(set) + '"></span> ' + Tools.escapeHTML(set.name || set.species) + '</li>';
buf += '<li' + (i === data.i ? ' style="opacity:.3"' : ' style="opacity:.6"') + '><span class="picon" style="display:inline-block;vertical-align:middle;' + Tools.getPokemonIcon(set) + '"></span> ' + BattleLog.escapeHTML(set.name || set.species) + '</li>';
}
if (i !== data.i && i !== data.i + 1) {
buf += '<li><button name="moveHere" value="' + i + '"><i class="fa fa-arrow-right"></i> Move here</button></li>';
@ -3387,7 +3387,7 @@
initialize: function (data) {
this.room = data.room;
this.folder = data.folder;
var buf = '<form><p>Remove "' + data.folder.slice(0, -1) + '"?</p><p><label><input type="checkbox" name="addname" /> Add "' + Tools.escapeHTML(this.folder.slice(0, -1)) + '" before team names</label></p>';
var buf = '<form><p>Remove "' + data.folder.slice(0, -1) + '"?</p><p><label><input type="checkbox" name="addname" /> Add "' + BattleLog.escapeHTML(this.folder.slice(0, -1)) + '" before team names</label></p>';
buf += '<p><button type="submit"><strong>Remove (keep teams)</strong></button> <!--button name="removeDelete"><strong>Remove (delete teams)</strong></button--> <button name="close" class="autofocus">Cancel</button></p></form>';
this.$el.html(buf);
},

View File

@ -30,11 +30,11 @@
updateUserbar: function () {
var buf = '';
var name = ' ' + app.user.get('name');
var color = hashColor(app.user.get('userid'));
var color = BattleLog.hashColor(app.user.get('userid'));
if (!app.user.loaded) {
buf = '<button disabled>Loading...</button>';
} else if (app.user.get('named')) {
buf = '<span class="username" data-name="' + Tools.escapeHTML(name) + '" style="' + color + '"><i class="fa fa-user" style="color:#779EC5"></i> ' + Tools.escapeHTML(name) + '</span>';
buf = '<span class="username" data-name="' + BattleLog.escapeHTML(name) + '" style="' + color + '"><i class="fa fa-user" style="color:#779EC5"></i> ' + BattleLog.escapeHTML(name) + '</span>';
} else {
buf = '<button name="login">Choose name</button>';
}
@ -74,7 +74,7 @@
if (room.notifications[tag].title) title += room.notifications[tag].title + '\n';
if (room.notifications[tag].body) title += room.notifications[tag].body + '\n';
}
if (title) buf += ' title="' + Tools.escapeHTML(title) + '"';
if (title) buf += ' title="' + BattleLog.escapeHTML(title) + '"';
}
switch (room ? room.type : id) {
case '':
@ -89,7 +89,7 @@
case 'rooms':
return buf + ' aria-label="Join chatroom"><i class="fa fa-plus" style="margin:7px auto -6px auto"></i> <span>&nbsp;</span></a></li>';
case 'battle':
var name = Tools.escapeHTML(room.title);
var name = BattleLog.escapeHTML(room.title);
var idChunks = id.substr(7).split('-');
var formatid;
if (idChunks.length <= 1) {
@ -101,25 +101,25 @@
var p1 = (room.battle && room.battle.p1 && room.battle.p1.name) || '';
var p2 = (room.battle && room.battle.p2 && room.battle.p2.name) || '';
if (p1 && p2) {
name = '' + Tools.escapeHTML(p1) + ' v. ' + Tools.escapeHTML(p2);
name = '' + BattleLog.escapeHTML(p1) + ' v. ' + BattleLog.escapeHTML(p2);
} else if (p1 || p2) {
name = '' + Tools.escapeHTML(p1) + Tools.escapeHTML(p2);
name = '' + BattleLog.escapeHTML(p1) + BattleLog.escapeHTML(p2);
} else {
name = '(empty room)';
}
}
return buf + ' draggable="true"><i class="text">' + Tools.escapeFormat(formatid) + '</i><span>' + name + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
return buf + ' draggable="true"><i class="text">' + BattleLog.escapeFormat(formatid) + '</i><span>' + name + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
case 'chat':
return buf + ' draggable="true"><i class="fa fa-comment-o"></i> <span>' + (Tools.escapeHTML(room.title) || (id === 'lobby' ? 'Lobby' : id)) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
return buf + ' draggable="true"><i class="fa fa-comment-o"></i> <span>' + (BattleLog.escapeHTML(room.title) || (id === 'lobby' ? 'Lobby' : id)) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
case 'html':
default:
if (room.title && room.title.charAt(0) === '[') {
var closeBracketIndex = room.title.indexOf(']');
if (closeBracketIndex > 0) {
return buf + ' draggable="true"><i class="text">' + Tools.escapeFormat(room.title.slice(1, closeBracketIndex)) + '</i><span>' + Tools.escapeHTML(room.title.slice(closeBracketIndex + 1)) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
return buf + ' draggable="true"><i class="text">' + BattleLog.escapeFormat(room.title.slice(1, closeBracketIndex)) + '</i><span>' + BattleLog.escapeHTML(room.title.slice(closeBracketIndex + 1)) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
}
}
return buf + ' draggable="true"><i class="fa fa-file-text-o"></i> <span>' + (Tools.escapeHTML(room.title) || id) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
return buf + ' draggable="true"><i class="fa fa-file-text-o"></i> <span>' + (BattleLog.escapeHTML(room.title) || id) + '</span></a><button class="closebutton" name="closeRoom" value="' + id + '" aria-label="Close"><i class="fa fa-times-circle"></i></a></li>';
}
},
updateTabbar: function () {
@ -456,7 +456,7 @@
var avatar = app.user.get('avatar');
var buf = '';
buf += '<p>' + (avatar ? '<img class="trainersprite" src="' + Tools.resolveAvatar(avatar) + '" width="40" height="40" style="vertical-align:middle;cursor:pointer" />' : '') + '<strong>' + Tools.escapeHTML(name) + '</strong></p>';
buf += '<p>' + (avatar ? '<img class="trainersprite" src="' + Tools.resolveAvatar(avatar) + '" width="40" height="40" style="vertical-align:middle;cursor:pointer" />' : '') + '<strong>' + BattleLog.escapeHTML(name) + '</strong></p>';
buf += '<p><button name="avatars">Change avatar</button></p>';
if (app.user.get('named')) {
var registered = app.user.get('registered');
@ -776,7 +776,7 @@
var buf = '<form>';
if (data.error) {
buf += '<p class="error">' + Tools.escapeHTML(data.error) + '</p>';
buf += '<p class="error">' + BattleLog.escapeHTML(data.error) + '</p>';
if (data.error.indexOf('inappropriate') >= 0) {
buf += '<p>Keep in mind these rules:</p>';
buf += '<ol>';
@ -786,14 +786,14 @@
buf += '</ol>';
}
} else if (data.reason) {
buf += '<p>' + Tools.parseMessage(data.reason) + '</p>';
buf += '<p>' + BattleLog.parseMessage(data.reason) + '</p>';
} else if (!data.force) {
var noRenameGames = '';
if (app.rooms[''].games) {
for (var roomid in app.rooms[''].games) {
var title = app.rooms[''].games[roomid];
if (title.slice(-1) === '*') {
noRenameGames += '<li>' + Tools.escapeHTML(title.slice(0, -1)) + '</li>';
noRenameGames += '<li>' + BattleLog.escapeHTML(title.slice(0, -1)) + '</li>';
}
}
}
@ -810,7 +810,7 @@
var name = (data.name || '');
if (!name && app.user.get('named')) name = app.user.get('name');
buf += '<p><label class="label">Username: <small class="preview" style="' + hashColor(toUserid(name)) + '">(color)</small><input class="textbox autofocus" type="text" name="username" value="' + Tools.escapeHTML(name) + '" autocomplete="username"></label></p>';
buf += '<p><label class="label">Username: <small class="preview" style="' + BattleLog.hashColor(toUserid(name)) + '">(color)</small><input class="textbox autofocus" type="text" name="username" value="' + BattleLog.escapeHTML(name) + '" autocomplete="username"></label></p>';
if (name) {
buf += '<p><small>(Others will be able to see your name change. To change name privately, use "Log out")</small></p>';
}
@ -825,7 +825,7 @@
updateColor: function (e) {
var name = e.currentTarget.value;
var preview = this.$('.preview');
var css = hashColor(toUserid(name)).slice(6, -1);
var css = BattleLog.hashColor(toUserid(name)).slice(6, -1);
preview.css('color', css);
},
force: function () {
@ -890,11 +890,11 @@
} else {
buf += '<p>Register your account:</p>';
}
buf += '<p><label class="label">Username: <strong><input type="text" name="name" value="' + Tools.escapeHTML(data.name || app.user.get('name')) + '" style="color:inherit;background:transparent;border:0;font:inherit;font-size:inherit;display:block" readonly autocomplete="username" /></strong></label></p>';
buf += '<p><label class="label">Username: <strong><input type="text" name="name" value="' + BattleLog.escapeHTML(data.name || app.user.get('name')) + '" style="color:inherit;background:transparent;border:0;font:inherit;font-size:inherit;display:block" readonly autocomplete="username" /></strong></label></p>';
buf += '<p><label class="label">Password: <input class="textbox autofocus" type="password" name="password" autocomplete="new-password" /></label></p>';
buf += '<p><label class="label">Password (confirm): <input class="textbox" type="password" name="cpassword" autocomplete="new-password" /></label></p>';
buf += '<p><label class="label"><img src="' + Tools.resourcePrefix + 'sprites/bwani/pikachu.gif" /></label></p>';
buf += '<p><label class="label">What is this pokemon? <input class="textbox" type="text" name="captcha" value="' + Tools.escapeHTML(data.captcha) + '" /></label></p>';
buf += '<p><label class="label">What is this pokemon? <input class="textbox" type="text" name="captcha" value="' + BattleLog.escapeHTML(data.captcha) + '" /></label></p>';
buf += '<p class="buttonbar"><button type="submit"><strong>Register</strong></button> <button name="close">Cancel</button></p></form>';
this.$el.html(buf);
},
@ -933,7 +933,7 @@
var buf = '<form>';
if (data.error) {
buf += '<p class="error">' + Tools.escapeHTML(data.error) + '</p>';
buf += '<p class="error">' + BattleLog.escapeHTML(data.error) + '</p>';
if (data.error.indexOf(' forced you to change ') >= 0) {
buf += '<p>Keep in mind these rules:</p>';
buf += '<ol>';
@ -943,13 +943,13 @@
buf += '</ol>';
}
} else if (data.reason) {
buf += '<p>' + Tools.escapeHTML(data.reason) + '</p>';
buf += '<p>' + BattleLog.escapeHTML(data.reason) + '</p>';
} else {
buf += '<p class="error">The name you chose is registered.</p>';
}
buf += '<p>If this is your account:</p>';
buf += '<p><label class="label">Username: <strong><input type="text" name="username" value="' + Tools.escapeHTML(data.username) + '" style="color:inherit;background:transparent;border:0;font:inherit;font-size:inherit;display:block" readonly autocomplete="username" /></strong></label></p>';
buf += '<p><label class="label">Username: <strong><input type="text" name="username" value="' + BattleLog.escapeHTML(data.username) + '" style="color:inherit;background:transparent;border:0;font:inherit;font-size:inherit;display:block" readonly autocomplete="username" /></strong></label></p>';
if (data.special === '@gmail') {
buf += '<div id="gapi-custom-signin" style="width:240px;margin:0 auto">[loading Google log-in button]</div>';
buf += '<p class="buttonbar"><button name="close">Cancel</button></p>';

View File

@ -831,7 +831,7 @@
var replayLink = 'https://replay.pokemonshowdown.com/' + replayid;
$.ajax(replayLink + '.json', {dataType: 'json'}).done(function (replay) {
if (replay) {
var title = Tools.escapeHTML(replay.p1) + ' vs. ' + Tools.escapeHTML(replay.p2);
var title = BattleLog.escapeHTML(replay.p1) + ' vs. ' + BattleLog.escapeHTML(replay.p2);
app.receive('>battle-' + replayid + '\n|init|battle\n|title|' + title + '\n' + replay.log);
app.receive('>battle-' + replayid + '\n|expire|<a href=' + replayLink + ' target="_blank">Open replay in new tab</a>');
} else {
@ -980,7 +980,7 @@
app.addPopup(Popup, {
type: type,
maxWidth: maxWidth,
htmlMessage: Tools.sanitizeHTML(data)
htmlMessage: BattleLog.sanitizeHTML(data)
});
} else {
app.addPopup(Popup, {
@ -993,7 +993,7 @@
break;
case 'disconnect':
app.trigger('init:socketclosed', Tools.sanitizeHTML(data.substr(12)));
app.trigger('init:socketclosed', BattleLog.sanitizeHTML(data.substr(12)));
break;
case 'pm':
@ -1060,7 +1060,7 @@
if (!groupName) Config.defaultGroup = symbol;
groups[symbol] = {
name: groupName ? Tools.escapeHTML(groupName + ' (' + symbol + ')') : null,
name: groupName ? BattleLog.escapeHTML(groupName + ' (' + symbol + ')') : null,
type: groupType,
order: i + 1
};
@ -2231,7 +2231,7 @@
},
initialize: function (data) {
if (!this.type) this.type = 'semimodal';
this.$el.html('<form><p style="white-space:pre-wrap;word-wrap:break-word">' + (data.htmlMessage || Tools.parseMessage(data.message)) + '</p><p class="buttonbar">' + (data.buttons || '<button name="close" class="autofocus"><strong>OK</strong></button>') + '</p></form>').css('max-width', data.maxWidth || 480);
this.$el.html('<form><p style="white-space:pre-wrap;word-wrap:break-word">' + (data.htmlMessage || BattleLog.parseMessage(data.message)) + '</p><p class="buttonbar">' + (data.buttons || '<button name="close" class="autofocus"><strong>OK</strong></button>') + '</p></form>').css('max-width', data.maxWidth || 480);
},
dispatchClickButton: function (e) {
@ -2284,7 +2284,7 @@
var buf = '<form>';
buf += '<p><label class="label">' + data.message;
buf += '<input class="textbox autofocus" type="text" name="data" value="' + Tools.escapeHTML(data.value || '') + '" /></label></p>';
buf += '<input class="textbox autofocus" type="text" name="data" value="' + BattleLog.escapeHTML(data.value || '') + '" /></label></p>';
buf += '<p class="buttonbar"><button type="submit"><strong>' + data.button + '</strong></button> <button name="close">Cancel</button></p>';
buf += '</form>';
@ -2403,7 +2403,7 @@
var buf = '<div class="userdetails">';
if (avatar) buf += '<img class="trainersprite' + (userid === ownUserid ? ' yours' : '') + '" src="' + Tools.resolveAvatar(avatar) + '" />';
buf += '<strong><a href="//pokemonshowdown.com/users/' + userid + '" target="_blank">' + Tools.escapeHTML(name) + '</a></strong><br />';
buf += '<strong><a href="//pokemonshowdown.com/users/' + userid + '" target="_blank">' + BattleLog.escapeHTML(name) + '</a></strong><br />';
buf += '<small>' + (group || '&nbsp;') + '</small>';
if (globalgroup) buf += '<br /><small>' + globalgroup + '</small>';
if (data.rooms) {
@ -2421,7 +2421,7 @@
var p1 = data.rooms[i].p1.substr(1);
var p2 = data.rooms[i].p2.substr(1);
var ownBattle = (ownUserid === toUserid(p1) || ownUserid === toUserid(p2));
var room = '<span title="' + (Tools.escapeHTML(p1) || '?') + ' v. ' + (Tools.escapeHTML(p2) || '?') + '">' + '<a href="' + app.root + roomid + '" class="ilink' + ((ownBattle || app.rooms[i]) ? ' yours' : '') + '">' + roomrank + roomid.substr(7) + '</a></span>';
var room = '<span title="' + (BattleLog.escapeHTML(p1) || '?') + ' v. ' + (BattleLog.escapeHTML(p2) || '?') + '">' + '<a href="' + app.root + roomid + '" class="ilink' + ((ownBattle || app.rooms[i]) ? ' yours' : '') + '">' + roomrank + roomid.substr(7) + '</a></span>';
if (data.rooms[i].isPrivate) {
if (!privatebuf) privatebuf = '<br /><em>Private rooms:</em> ';
else privatebuf += ', ';
@ -2524,7 +2524,7 @@
}
var $pm = $('.pm-window-' + this.userid);
if ($pm.length && $pm.css('display') !== 'none') {
$pm.find('.inner').append('<div class="chat">' + Tools.escapeHTML(buf) + '</div>');
$pm.find('.inner').append('<div class="chat">' + BattleLog.escapeHTML(buf) + '</div>');
} else {
var room = (app.curRoom && app.curRoom.add ? app.curRoom : app.curSideRoom);
if (!room || !room.add) {
@ -2619,7 +2619,7 @@
var warning = ('warning' in data);
var buf = '';
if (warning) {
buf += '<p><strong style="color:red">' + (Tools.escapeHTML(data.warning) || 'You have been warned for breaking the rules.') + '</strong></p>';
buf += '<p><strong style="color:red">' + (BattleLog.escapeHTML(data.warning) || 'You have been warned for breaking the rules.') + '</strong></p>';
}
buf += '<h2>Pok&eacute;mon Showdown Rules</h2>';
buf += '<b>Global</b><br /><br /><b>1.</b> Be nice to people. Respect people. Don\'t be rude or mean to people.<br /><br /><b>2.</b> Follow US laws (PS is based in the US). No porn (minors use PS), don\'t distribute pirated material, and don\'t slander others.<br /><br /><b>3.</b>&nbsp;No sex. Don\'t discuss anything sexually explicit, not even in private messages, not even if you\'re both adults.<br /><b></b><br /><b>4.</b>&nbsp;No cheating. Don\'t exploit bugs to gain an unfair advantage. Don\'t game the system (by intentionally losing against yourself or a friend in a ladder match, by timerstalling, etc). Don\'t impersonate staff if you\'re not.<br /><br /><b>5.</b> Moderators have discretion to punish any behaviour they deem inappropriate, whether or not it\'s on this list. If you disagree with a moderator ruling, appeal to a leader (a user with &amp; next to their name) or <a href="https://pokemonshowdown.com/appeal">Discipline Appeals</a>.<br /><br />(Note: The First Amendment does not apply to PS, since PS is not a government organization.)<br /><br />';

View File

@ -125,7 +125,7 @@ var Replays = {
var replayid = this.$('input[name=replayid]').val();
var m = /^([a-z0-9]+)-[a-z0-9]+-[0-9]+$/.exec(replayid);
if (m) {
this.battle.log('<hr /><div class="chat">This replay was uploaded from a third-party server (<code>' + Tools.escapeHTML(m[1]) + '</code>). It contains errors and cannot be viewed.</div><div class="chat">Replays uploaded from third-party servers can contain errors if the server is running custom code, or the server operator has otherwise incorrectly configured their server.</div>', true);
this.battle.log('<hr /><div class="chat">This replay was uploaded from a third-party server (<code>' + BattleLog.escapeHTML(m[1]) + '</code>). It contains errors and cannot be viewed.</div><div class="chat">Replays uploaded from third-party servers can contain errors if the server is running custom code, or the server operator has otherwise incorrectly configured their server.</div>', true);
this.battle.pause();
}
},

View File

@ -541,7 +541,7 @@
var text = this.filters[i][1];
if (this.filters[i][0] === 'move') text = Tools.getMove(text).name;
if (this.filters[i][0] === 'pokemon') text = Tools.getTemplate(text).name;
buf += '<button class="filter" value="' + Tools.escapeHTML(this.filters[i].join(':')) + '">' + text + ' <i class="fa fa-times-circle"></i></button> ';
buf += '<button class="filter" value="' + BattleLog.escapeHTML(this.filters[i].join(':')) + '">' + text + ' <i class="fa fa-times-circle"></i></button> ';
}
if (!q) buf += '<small style="color: #888">(backspace = delete filter)</small>';
return buf + '</p>';
@ -878,7 +878,7 @@
template = Tools.getTemplate(set.species);
var abilitySet = [['header', "Abilities"]];
if (template.isMega) {
abilitySet.unshift(['html', '<p>Will be <strong>' + Tools.escapeHTML(template.abilities['0']) + '</strong> after Mega Evolving.</p>']);
abilitySet.unshift(['html', '<p>Will be <strong>' + BattleLog.escapeHTML(template.abilities['0']) + '</strong> after Mega Evolving.</p>']);
template = Tools.getTemplate(template.baseSpecies);
}
var abilitiesInThisGen = Tools.getAbilitiesFor(template.id, this.gen);
@ -898,7 +898,7 @@
template = Tools.getTemplate(set.species);
var abilities = [];
if (template.isMega) {
if (format === 'almostanyability') abilitySet.unshift(['html', '<p>Will be <strong>' + Tools.escapeHTML(template.abilities['0']) + '</strong> after Mega Evolving.</p>']);
if (format === 'almostanyability') abilitySet.unshift(['html', '<p>Will be <strong>' + BattleLog.escapeHTML(template.abilities['0']) + '</strong> after Mega Evolving.</p>']);
// template is unused after this, so no need to replace
}
for (var i in BattleAbilities) {
@ -1290,7 +1290,7 @@
if (!pokemon) return '<li class="result">Unrecognized pokemon</li>';
var id = toId(pokemon.species);
if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'pokemon/' + id + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="pokemon|' + Tools.escapeHTML(pokemon.species) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="pokemon|' + BattleLog.escapeHTML(pokemon.species) + '">';
// number
// buf += '<span class="col numcol">' + (pokemon.num >= 0 ? pokemon.num : 'CAP') + '</span> ';
@ -1414,7 +1414,7 @@
Search.prototype.renderTaggedPokemonRowInner = function (pokemon, tag, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'pokemon/' + toId(pokemon.species) + '" data-target="push"';
var buf = '<a' + attrs + ' data-entry="pokemon|' + Tools.escapeHTML(pokemon.species) + '">';
var buf = '<a' + attrs + ' data-entry="pokemon|' + BattleLog.escapeHTML(pokemon.species) + '">';
// tag
buf += '<span class="col tagcol shorttagcol">' + tag + '</span> ';
@ -1485,7 +1485,7 @@
if (!item) return '<li class="result">Unrecognized item</li>';
var id = toId(item.name);
if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'items/' + id + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="item|' + Tools.escapeHTML(item.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="item|' + BattleLog.escapeHTML(item.name) + '">';
// icon
buf += '<span class="col itemiconcol">';
@ -1515,7 +1515,7 @@
}
}
}
buf += '<span class="col itemdesccol">' + Tools.escapeHTML(desc) + '</span> ';
buf += '<span class="col itemdesccol">' + BattleLog.escapeHTML(desc) + '</span> ';
buf += '</a></li>';
@ -1526,7 +1526,7 @@
if (!ability) return '<li class="result">Unrecognized ability</li>';
var id = toId(ability.name);
if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'abilities/' + id + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="ability|' + Tools.escapeHTML(ability.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="ability|' + BattleLog.escapeHTML(ability.name) + '">';
// name
var name = ability.name;
@ -1541,7 +1541,7 @@
return buf;
}
buf += '<span class="col abilitydesccol">' + Tools.escapeHTML(ability.shortDesc || ability.desc) + '</span> ';
buf += '<span class="col abilitydesccol">' + BattleLog.escapeHTML(ability.shortDesc || ability.desc) + '</span> ';
buf += '</a></li>';
@ -1552,7 +1552,7 @@
if (!move) return '<li class="result">Unrecognized move</li>';
var id = toId(move.name);
if (Search.urlRoot) attrs += ' href="' + Search.urlRoot + 'moves/' + id + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="move|' + Tools.escapeHTML(move.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="move|' + BattleLog.escapeHTML(move.name) + '">';
// name
var name = move.name;
@ -1614,7 +1614,7 @@
}
}
}
buf += '<span class="col movedesccol">' + Tools.escapeHTML(desc) + '</span> ';
buf += '<span class="col movedesccol">' + BattleLog.escapeHTML(desc) + '</span> ';
buf += '</a></li>';
@ -1623,7 +1623,7 @@
Search.prototype.renderMoveRowInner = function (move, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'moves/' + toId(move.name) + '" data-target="push"';
var buf = '<a' + attrs + ' data-entry="move|' + Tools.escapeHTML(move.name) + '">';
var buf = '<a' + attrs + ' data-entry="move|' + BattleLog.escapeHTML(move.name) + '">';
// name
var name = move.name;
@ -1649,7 +1649,7 @@
buf += '<span class="col pplabelcol"><em>PP</em><br />' + (move.pp === 1 || move.noPPBoosts ? move.pp : move.pp * 8 / 5) + '</span> ';
// desc
buf += '<span class="col movedesccol">' + Tools.escapeHTML(move.shortDesc || move.desc) + '</span> ';
buf += '<span class="col movedesccol">' + BattleLog.escapeHTML(move.shortDesc || move.desc) + '</span> ';
buf += '</a>';
@ -1658,7 +1658,7 @@
Search.prototype.renderTaggedMoveRow = function (move, tag, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'moves/' + toId(move.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="move|' + Tools.escapeHTML(move.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="move|' + BattleLog.escapeHTML(move.name) + '">';
// tag
buf += '<span class="col tagcol">' + tag + '</span> ';
@ -1686,7 +1686,7 @@
buf += '<span class="col pplabelcol"><em>PP</em><br />' + (move.pp !== 1 ? move.pp * 8 / 5 : move.pp) + '</span> ';
// desc
buf += '<span class="col movedesccol">' + Tools.escapeHTML(move.shortDesc || move.desc) + '</span> ';
buf += '<span class="col movedesccol">' + BattleLog.escapeHTML(move.shortDesc || move.desc) + '</span> ';
buf += '</a></li>';
@ -1696,7 +1696,7 @@
Search.prototype.renderTypeRow = function (type, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'types/' + toId(type.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="type|' + Tools.escapeHTML(type.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="type|' + BattleLog.escapeHTML(type.name) + '">';
// name
var name = type.name;
@ -1723,7 +1723,7 @@
Search.prototype.renderCategoryRow = function (category, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'categories/' + category.id + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="category|' + Tools.escapeHTML(category.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="category|' + BattleLog.escapeHTML(category.name) + '">';
// name
var name = category.name;
@ -1752,7 +1752,7 @@
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'articles/' + article.id + '" data-target="push"';
var isSearchType = (article.id === 'pokemon' || article.id === 'moves');
if (isSearchType) attrs = ' href="' + article.id + '/" data-target="replace"';
var buf = '<li class="result"><a' + attrs + ' data-entry="article|' + Tools.escapeHTML(article.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="article|' + BattleLog.escapeHTML(article.name) + '">';
// name
var name = article.name;
@ -1781,7 +1781,7 @@
Search.prototype.renderEggGroupRow = function (egggroup, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'egggroups/' + toId(egggroup.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="egggroup|' + Tools.escapeHTML(egggroup.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="egggroup|' + BattleLog.escapeHTML(egggroup.name) + '">';
// name
var name = egggroup.name;
@ -1803,7 +1803,7 @@
Search.prototype.renderTierRow = function (tier, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'tiers/' + toId(tier.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-entry="tier|' + Tools.escapeHTML(tier.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="tier|' + BattleLog.escapeHTML(tier.name) + '">';
// name
var name = tier.name;

View File

@ -486,10 +486,10 @@ Storage.initTestClient = function () {
if (uri[0] === '/') { //relative URI
uri = Tools.resourcePrefix + uri.substr(1);
}
var src = '<!DOCTYPE html><html><body><form action="' + Tools.escapeHTML(uri) + '" method="POST">';
var src = '<!DOCTYPE html><html><body><form action="' + BattleLog.escapeHTML(uri) + '" method="POST">';
src += '<input type="hidden" name="testclient">';
for (var i in data) {
src += '<input type=hidden name="' + i + '" value="' + Tools.escapeHTML(data[i]) + '">';
src += '<input type=hidden name="' + i + '" value="' + BattleLog.escapeHTML(data[i]) + '">';
}
src += '<input type=submit value="Please click this button first."></form></body></html>';
app.addPopup(ProxyPopup, {uri: "data:text/html;charset=UTF-8," + encodeURIComponent(src), callback: callback});
@ -529,7 +529,7 @@ Storage.loadPackedTeams = function (buffer) {
Storage.whenAppLoaded(function (app) {
app.addPopup(Popup, {
type: 'modal',
htmlMessage: "Your teams are corrupt and could not be loaded. :( We may be able to recover a team from this data:<br /><textarea rows=\"10\" cols=\"60\">" + Tools.escapeHTML(buffer) + "</textarea>"
htmlMessage: "Your teams are corrupt and could not be loaded. :( We may be able to recover a team from this data:<br /><textarea rows=\"10\" cols=\"60\">" + BattleLog.escapeHTML(buffer) + "</textarea>"
});
});
}

View File

@ -657,7 +657,7 @@ class BattleScene {
pokemonhtml = '<div class="teamicons">' + pokemonhtml + '</div>';
const $sidebar = (side.n ? this.$rightbar : this.$leftbar);
if (side.name) {
$sidebar.html('<div class="trainer"><strong>' + Tools.escapeHTML(side.name) + '</strong><div class="trainersprite" style="background-image:url(' + Tools.resolveAvatar(side.spriteid) + ')"></div>' + pokemonhtml + '</div>');
$sidebar.html('<div class="trainer"><strong>' + BattleLog.escapeHTML(side.name) + '</strong><div class="trainersprite" style="background-image:url(' + Tools.resolveAvatar(side.spriteid) + ')"></div>' + pokemonhtml + '</div>');
$sidebar.find('.trainer').css('opacity', 1);
} else {
$sidebar.find('.trainer').css('opacity', 0.4);
@ -729,7 +729,7 @@ class BattleScene {
}
side.totalPokemon = side.pokemon.length;
if (textBuf) {
this.log('<div class="chat battle-history"><strong>' + Tools.escapeHTML(side.name) + '\'s team:</strong> <em style="color:#445566;display:block;">' + Tools.escapeHTML(textBuf) + '</em></div>');
this.log('<div class="chat battle-history"><strong>' + BattleLog.escapeHTML(side.name) + '\'s team:</strong> <em style="color:#445566;display:block;">' + BattleLog.escapeHTML(textBuf) + '</em></div>');
}
this.$sprites[siden].html(buf + buf2);
@ -2270,7 +2270,7 @@ class PokemonSprite extends Sprite {
getStatbarHTML(pokemon: Pokemon) {
let buf = '<div class="statbar' + (this.siden ? ' lstatbar' : ' rstatbar') + '" style="display: none">';
buf += '<strong>' + (this.siden && (this.scene.battle.ignoreOpponent || this.scene.battle.ignoreNicks) ? pokemon.species : Tools.escapeHTML(pokemon.name));
buf += '<strong>' + (this.siden && (this.scene.battle.ignoreOpponent || this.scene.battle.ignoreNicks) ? pokemon.species : BattleLog.escapeHTML(pokemon.name));
let gender = pokemon.gender;
if (gender) buf += ' <img src="' + Tools.resourcePrefix + 'fx/gender-' + gender.toLowerCase() + '.png" alt="' + gender + '" />';
buf += (pokemon.level === 100 ? '' : ' <small>L' + pokemon.level + '</small>');

View File

@ -89,55 +89,6 @@ if (window.soundManager) {
// @ts-ignore
window.nodewebkit = !!(typeof process !== 'undefined' && process.versions && process.versions['node-webkit']);
let colorCache = {} as {[userid: string]: string};
function hashColor(name: string) {
if (colorCache[name]) return colorCache[name];
let hash;
if (window.Config && Config.customcolors && Config.customcolors[name]) {
if (Config.customcolors[name].color) {
return (colorCache[name] = 'color:' + Config.customcolors[name].color + ';');
}
hash = MD5(Config.customcolors[name]);
} else {
hash = MD5(name);
}
let H = parseInt(hash.substr(4, 4), 16) % 360; // 0 to 360
let S = parseInt(hash.substr(0, 4), 16) % 50 + 40; // 40 to 89
let L = Math.floor(parseInt(hash.substr(8, 4), 16) % 20 + 30); // 30 to 49
let C = (100 - Math.abs(2 * L - 100)) * S / 100 / 100;
let X = C * (1 - Math.abs((H / 60) % 2 - 1));
let m = L / 100 - C / 2;
let R1, G1, B1;
switch (Math.floor(H / 60)) {
case 1: R1 = X; G1 = C; B1 = 0; break;
case 2: R1 = 0; G1 = C; B1 = X; break;
case 3: R1 = 0; G1 = X; B1 = C; break;
case 4: R1 = X; G1 = 0; B1 = C; break;
case 5: R1 = C; G1 = 0; B1 = X; break;
case 0: default: R1 = C; G1 = X; B1 = 0; break;
}
let R = R1 + m, G = G1 + m, B = B1 + m;
let lum = R * R * R * 0.2126 + G * G * G * 0.7152 + B * B * B * 0.0722; // 0.013 (dark blue) to 0.737 (yellow)
let HLmod = (lum - 0.2) * -150; // -80 (yellow) to 28 (dark blue)
if (HLmod > 18) HLmod = (HLmod - 18) * 2.5;
else if (HLmod < 0) HLmod = (HLmod - 0) / 3;
else HLmod = 0;
// let mod = ';border-right: ' + Math.abs(HLmod) + 'px solid ' + (HLmod > 0 ? 'red' : '#0088FF');
let Hdist = Math.min(Math.abs(180 - H), Math.abs(240 - H));
if (Hdist < 15) {
HLmod += (15 - Hdist) / 3;
}
L += HLmod;
colorCache[name] = "color:hsl(" + H + "," + S + "%," + L + "%);";
return colorCache[name];
}
function getString(str: any) {
if (typeof str === 'string' || typeof str === 'number') return '' + str;
return '';
@ -268,372 +219,9 @@ const Tools = {
return Tools.resolveAvatar(sprites[Math.floor(Math.random() * sprites.length)]);
},
escapeFormat(formatid: string): string {
let atIndex = formatid.indexOf('@@@');
if (atIndex >= 0) {
return Tools.escapeFormat(formatid.slice(0, atIndex)) + '<br />Custom rules: ' + Tools.escapeHTML(formatid.slice(atIndex + 3));
}
if (window.BattleFormats && BattleFormats[formatid]) {
return Tools.escapeHTML(BattleFormats[formatid].name);
}
return Tools.escapeHTML(formatid);
sanitizeName(name: string) {
return name.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
},
parseChatMessage(message: string, name: string, timestamp: string, isHighlighted?: boolean, $chatElem?: any) {
let showMe = !((Tools.prefs('chatformatting') || {}).hideme);
let group = ' ';
if (!/[A-Za-z0-9]/.test(name.charAt(0))) {
// Backwards compatibility
group = name.charAt(0);
name = name.substr(1);
}
let color = hashColor(toId(name));
let clickableName = '<small>' + Tools.escapeHTML(group) + '</small><span class="username" data-name="' + Tools.escapeHTML(name) + '">' + Tools.escapeHTML(name) + '</span>';
let hlClass = isHighlighted ? ' highlighted' : '';
let mineClass = (window.app && app.user && app.user.get('name') === name ? ' mine' : '');
let cmd = '';
let target = '';
if (message.charAt(0) === '/') {
if (message.charAt(1) === '/') {
message = message.slice(1);
} else {
let spaceIndex = message.indexOf(' ');
cmd = (spaceIndex >= 0 ? message.slice(1, spaceIndex) : message.slice(1));
if (spaceIndex >= 0) target = message.slice(spaceIndex + 1);
}
}
switch (cmd) {
case 'me':
if (!showMe) return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>/me' + Tools.parseMessage(' ' + target) + '</em></div>';
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">&bull;</strong> <em>' + clickableName + '<i>' + Tools.parseMessage(' ' + target) + '</i></em></div>';
case 'mee':
if (!showMe) return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>/me' + Tools.parseMessage(' ' + target).slice(1) + '</em></div>';
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">&bull;</strong> <em>' + clickableName + '<i>' + Tools.parseMessage(' ' + target).slice(1) + '</i></em></div>';
case 'invite':
let roomid = toRoomid(target);
return [
'<div class="chat">' + timestamp + '<em>' + clickableName + ' invited you to join the room "' + roomid + '"</em></div>',
'<div class="notice"><button name="joinRoom" value="' + roomid + '">Join ' + roomid + '</button></div>'
];
case 'announce':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <span class="message-announce">' + Tools.parseMessage(target) + '</span></div>';
case 'log':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<span class="message-log">' + Tools.parseMessage(target) + '</span></div>';
case 'data-pokemon':
let buf = '<li class="result">';
let template = Tools.getTemplate(target);
if (!template.abilities || !template.baseStats) return '[not supported in replays]';
buf += '<span class="col numcol">' + (template.tier || Tools.getTemplate(template.baseSpecies).tier) + '</span> ';
buf += '<span class="col iconcol"><span style="' + Tools.getPokemonIcon(template) + '"></span></span> ';
buf += '<span class="col pokemonnamecol" style="white-space:nowrap"><a href="https://pokemonshowdown.com/dex/pokemon/' + template.id + '" target="_blank">' + template.species + '</a></span> ';
buf += '<span class="col typecol">';
if (template.types) for (let i = 0; i < template.types.length; i++) {
buf += Tools.getTypeIcon(template.types[i]);
}
buf += '</span> ';
buf += '<span style="float:left;min-height:26px">';
if (template.abilities['1']) {
buf += '<span class="col twoabilitycol">' + template.abilities['0'] + '<br />' + template.abilities['1'] + '</span>';
} else {
buf += '<span class="col abilitycol">' + template.abilities['0'] + '</span>';
}
if (template.abilities['S']) {
buf += '<span class="col twoabilitycol' + (template.unreleasedHidden ? ' unreleasedhacol' : '') + '"><em>' + template.abilities['H'] + '<br />' + template.abilities['S'] + '</em></span>';
} else if (template.abilities['H']) {
buf += '<span class="col abilitycol' + (template.unreleasedHidden ? ' unreleasedhacol' : '') + '"><em>' + template.abilities['H'] + '</em></span>';
} else {
buf += '<span class="col abilitycol"></span>';
}
buf += '</span>';
buf += '<span style="float:left;min-height:26px">';
buf += '<span class="col statcol"><em>HP</em><br />' + template.baseStats.hp + '</span> ';
buf += '<span class="col statcol"><em>Atk</em><br />' + template.baseStats.atk + '</span> ';
buf += '<span class="col statcol"><em>Def</em><br />' + template.baseStats.def + '</span> ';
buf += '<span class="col statcol"><em>SpA</em><br />' + template.baseStats.spa + '</span> ';
buf += '<span class="col statcol"><em>SpD</em><br />' + template.baseStats.spd + '</span> ';
buf += '<span class="col statcol"><em>Spe</em><br />' + template.baseStats.spe + '</span> ';
let bst = 0;
for (const i in template.baseStats) bst += template.baseStats[i as StatName];
buf += '<span class="col bstcol"><em>BST<br />' + bst + '</em></span> ';
buf += '</span>';
buf += '</li>';
return '<div class="message"><ul class="utilichart">' + buf + '<li style=\"clear:both\"></li></ul></div>';
case 'data-item':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderItemRow(Tools.getItem(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'data-ability':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderAbilityRow(Tools.getAbility(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'data-move':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderMoveRow(Tools.getMove(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'text':
return '<div class="chat">' + Tools.parseMessage(target) + '</div>';
case 'error':
return '<div class="chat message-error">' + Tools.escapeHTML(target) + '</div>';
case 'html':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + Tools.sanitizeHTML(target) + '</em></div>';
case 'uhtml':
case 'uhtmlchange':
let parts = target.split(',');
let $elements = $chatElem.find('div.uhtml-' + toId(parts[0]));
let html = parts.slice(1).join(',');
if (!html) {
$elements.remove();
} else if (!$elements.length) {
$chatElem.append('<div class="chat uhtml-' + toId(parts[0]) + '">' + Tools.sanitizeHTML(html) + '</div>');
} else if (cmd === 'uhtmlchange') {
$elements.html(Tools.sanitizeHTML(html));
} else {
$elements.remove();
$chatElem.append('<div class="chat uhtml-' + toId(parts[0]) + '">' + Tools.sanitizeHTML(html) + '</div>');
}
return '';
case 'raw':
return '<div class="chat">' + Tools.sanitizeHTML(target) + '</div>';
default:
// Not a command or unsupported. Parsed as a normal chat message.
if (!name) {
return '<div class="chat' + hlClass + '">' + timestamp + '<em>' + Tools.parseMessage(message) + '</em></div>';
}
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + Tools.parseMessage(message) + '</em></div>';
}
},
parseMessage(str: string) {
// Don't format console commands (>>).
if (str.substr(0, 3) === '>> ' || str.substr(0, 4) === '>>> ') return Tools.escapeHTML(str);
// Don't format console results (<<).
if (str.substr(0, 3) === '<< ') return Tools.escapeHTML(str);
str = formatText(str);
let options = Tools.prefs('chatformatting') || {};
if (options.hidelinks) {
str = str.replace(/<a[^>]*>/g, '<u>').replace(/<\/a>/g, '</u>');
}
if (options.hidespoiler) {
str = str.replace(/<span class="spoiler">/g, '<span class="spoiler spoiler-shown">');
}
if (options.hidegreentext) {
str = str.replace(/<span class="greentext">/g, '<span>');
}
return str;
},
escapeHTML(str: string, jsEscapeToo?: boolean) {
str = getString(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
if (jsEscapeToo) str = str.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
return str;
},
unescapeHTML(str: string) {
str = (str ? '' + str : '');
return str.replace(/&quot;/g, '"').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
},
escapeRegExp(str: string) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
},
sanitizeHTML: (function () {
if (!('html4' in window)) {
return function () {
throw new Error('sanitizeHTML requires caja');
};
}
// Add <marquee> <blink> <psicon> to the whitelist.
Object.assign(html4.ELEMENTS, {
'marquee': 0,
'blink': 0,
'psicon': html4.eflags['OPTIONAL_ENDTAG'] | html4.eflags['EMPTY']
});
Object.assign(html4.ATTRIBS, {
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee
'marquee::behavior': 0,
'marquee::bgcolor': 0,
'marquee::direction': 0,
'marquee::height': 0,
'marquee::hspace': 0,
'marquee::loop': 0,
'marquee::scrollamount': 0,
'marquee::scrolldelay': 0,
'marquee::truespeed': 0,
'marquee::vspace': 0,
'marquee::width': 0,
'psicon::pokemon': 0,
'psicon::item': 0
});
let uriRewriter = function (urlData: any) {
if (urlData.scheme_ === 'geo' || urlData.scheme_ === 'sms' || urlData.scheme_ === 'tel') return null;
return urlData;
};
let tagPolicy = function (tagName: string, attribs: string[]) {
if (html4.ELEMENTS[tagName] & html4.eflags['UNSAFE']) {
return;
}
let targetIdx = 0, srcIdx = 0;
if (tagName === 'a') {
// Special handling of <a> tags.
for (let i = 0; i < attribs.length - 1; i += 2) {
switch (attribs[i]) {
case 'target':
targetIdx = i + 1;
break;
}
}
}
let dataUri = '';
if (tagName === 'img') {
for (let i = 0; i < attribs.length - 1; i += 2) {
if (attribs[i] === 'src' && attribs[i + 1].substr(0, 11) === 'data:image/') {
srcIdx = i;
dataUri = attribs[i + 1];
}
if (attribs[i] === 'src' && attribs[i + 1].substr(0, 2) === '//') {
if (location.protocol !== 'http:' && location.protocol !== 'https:') {
attribs[i + 1] = 'http:' + attribs[i + 1];
}
}
}
} else if (tagName === 'psicon') {
// <psicon> is a custom element which supports a set of mutually incompatible attributes:
// <psicon pokemon> and <psicon item>
let classValueIndex = -1;
let styleValueIndex = -1;
let iconAttrib = null;
for (let i = 0; i < attribs.length - 1; i += 2) {
if (attribs[i] === 'pokemon' || attribs[i] === 'item') {
// If declared more than once, use the later.
iconAttrib = attribs.slice(i, i + 2);
} else if (attribs[i] === 'class') {
classValueIndex = i + 1;
} else if (attribs[i] === 'style') {
styleValueIndex = i + 1;
}
}
tagName = 'span';
if (iconAttrib) {
if (classValueIndex < 0) {
attribs.push('class', '');
classValueIndex = attribs.length - 1;
}
if (styleValueIndex < 0) {
attribs.push('style', '');
styleValueIndex = attribs.length - 1;
}
// Prepend all the classes and styles associated to the custom element.
if (iconAttrib[0] === 'pokemon') {
attribs[classValueIndex] = attribs[classValueIndex] ? 'picon ' + attribs[classValueIndex] : 'picon';
attribs[styleValueIndex] = attribs[styleValueIndex] ? Tools.getPokemonIcon(iconAttrib[1]) + '; ' + attribs[styleValueIndex] : Tools.getPokemonIcon(iconAttrib[1]);
} else if (iconAttrib[0] === 'item') {
attribs[classValueIndex] = attribs[classValueIndex] ? 'itemicon ' + attribs[classValueIndex] : 'itemicon';
attribs[styleValueIndex] = attribs[styleValueIndex] ? Tools.getItemIcon(iconAttrib[1]) + '; ' + attribs[styleValueIndex] : Tools.getItemIcon(iconAttrib[1]);
}
}
}
if (attribs[targetIdx] === 'replace') {
targetIdx = -targetIdx;
}
attribs = html.sanitizeAttribs(tagName, attribs, uriRewriter);
if (targetIdx < 0) {
targetIdx = -targetIdx;
attribs[targetIdx - 1] = 'data-target';
attribs[targetIdx] = 'replace';
targetIdx = 0;
}
if (dataUri && tagName === 'img') {
attribs[srcIdx + 1] = dataUri;
}
if (tagName === 'a' || tagName === 'form') {
if (targetIdx) {
attribs[targetIdx] = '_blank';
} else {
attribs.push('target');
attribs.push('_blank');
}
if (tagName === 'a') {
attribs.push('rel');
attribs.push('noopener');
}
}
return {tagName: tagName, attribs: attribs};
};
let localizeTime = function (full: string, date: string, time: string, timezone?: string) {
let parsedTime = new Date(date + 'T' + time + (timezone || 'Z').toUpperCase());
// Very old (pre-ES5) web browsers may be incapable of parsing ISO 8601
// dates. In such a case, gracefully continue without replacing the date
// format.
if (!parsedTime.getTime()) return full;
let formattedTime;
// Try using Intl API if it exists
if (window.Intl && Intl.DateTimeFormat) {
formattedTime = new Intl.DateTimeFormat(undefined, {month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'}).format(parsedTime);
} else {
// toLocaleString even exists in ECMAScript 1, so no need to check
// if it exists.
formattedTime = parsedTime.toLocaleString();
}
return '<time>' + Tools.escapeHTML(formattedTime) + '</time>';
};
return function (input: any) {
// <time> parsing requires ISO 8601 time. While more time formats are
// supported by most JavaScript implementations, it isn't required, and
// how to exactly enforce ignoring user agent timezone setting is not obvious.
// As dates come from the server which isn't aware of client timezone, a
// particular timezone is required.
//
// This regular expression is split into three groups.
//
// Group 1 - date
// Group 2 - time (seconds and milliseconds are optional)
// Group 3 - optional timezone
//
// Group 1 and group 2 are split to allow using space as a separator
// instead of T. Stricly speaking ECMAScript 5 specification only
// allows T, however it's more practical to also allow spaces.
return html.sanitizeWithPolicy(getString(input), tagPolicy)
.replace(/<time>\s*([+-]?\d{4,}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?)(Z|[+-]\d{2}:\d{2})?\s*<\/time>/ig, localizeTime);
};
})(),
interstice: (function () {
let patterns = (function (whitelist) {
let patterns = [];
for (let i = 0; i < whitelist.length; ++i) {
patterns.push(new RegExp('^(https?:)?//([A-Za-z0-9-]*\\.)?' +
whitelist[i] +
'(/.*)?', 'i'));
}
return patterns;
})((window.Config && Config.whitelist) ? Config.whitelist : []);
return {
isWhitelisted(uri: string) {
if ((uri[0] === '/') && (uri[1] !== '/')) {
// domain-relative URIs are safe
return true;
}
for (let i = 0; i < patterns.length; ++i) {
if (patterns[i].test(uri)) {
return true;
}
}
return false;
},
getURI(uri: string) {
return 'http://pokemonshowdown.com/interstice?uri=' + encodeURIComponent(uri);
}
};
})(),
prefs(prop: string, value?: any, save?: boolean) {
// @ts-ignore
@ -685,7 +273,7 @@ const Tools = {
effect.exists = true;
}
if (!effect.id) effect.id = id;
if (!effect.name) effect.name = Tools.escapeHTML(name);
if (!effect.name) effect.name = Tools.sanitizeName(name);
if (!effect.category) effect.category = 'Effect';
if (!effect.effectType) effect.effectType = 'Effect';
}
@ -717,7 +305,7 @@ const Tools = {
}
if (!move.id) move.id = id;
if (!move.name) move.name = Tools.escapeHTML(name);
if (!move.name) move.name = Tools.sanitizeName(name);
if (!move.critRatio) move.critRatio = 1;
if (!move.baseType) move.baseType = move.type;
@ -759,7 +347,7 @@ const Tools = {
item = (window.BattleItems && window.BattleItems[id]) || {};
if (item.name) item.exists = true;
if (!item.id) item.id = id;
if (!item.name) item.name = Tools.escapeHTML(name);
if (!item.name) item.name = Tools.sanitizeName(name);
if (!item.category) item.category = 'Effect';
if (!item.effectType) item.effectType = 'Item';
if (!item.gen) {
@ -784,7 +372,7 @@ const Tools = {
ability = (window.BattleAbilities && window.BattleAbilities[id]) || {};
if (ability.name) ability.exists = true;
if (!ability.id) ability.id = id;
if (!ability.name) ability.name = Tools.escapeHTML(name);
if (!ability.name) ability.name = Tools.sanitizeName(name);
if (!ability.category) ability.category = 'Effect';
if (!ability.effectType) ability.effectType = 'Ability';
if (!ability.gen) {
@ -840,7 +428,7 @@ const Tools = {
if (template.species) name = template.species;
if (template.exists === undefined) template.exists = true;
if (!template.id) template.id = id;
if (!template.name) template.name = name = Tools.escapeHTML(name);
if (!template.name) template.name = name = Tools.sanitizeName(name);
if (!template.speciesid) template.speciesid = id;
if (!template.species) template.species = name;
if (!template.baseSpecies) template.baseSpecies = name;
@ -1545,74 +1133,4 @@ const Tools = {
let sanitizedType = type.replace(/\?/g, '%3f');
return '<img src="' + Tools.resourcePrefix + 'sprites/types/' + sanitizedType + '.png" alt="' + type + '" height="14" width="32"' + (b ? ' class="b"' : '') + ' />';
},
/*********************************************************
* Replay files
*********************************************************/
// Replay files are .html files that display a replay for a battle.
// The .html files mainly contain replay log data; the actual replay
// player is downloaded online. Also included is a textual log and
// some minimal CSS to make it look pretty, for offline viewing.
// This strategy helps keep the replay file reasonably small; of
// the 30 KB or so for a 50-turn battle, around 10 KB is the log
// data, and around 20 KB is the textual log.
// The actual replay player is downloaded from replay-embed.js,
// which handles loading all the necessary resources for turning the log
// data into a playable replay.
// Battle log data is stored in and loaded from a
// <script type="text/plain" class="battle-log-data"> tag.
// replay-embed.js is loaded through a cache-buster that rotates daily.
// This allows pretty much anything about the replay viewer to be
// updated as desired.
createReplayFile(room: any) {
let battle = room.battle;
let replayid = room.id;
if (replayid) {
// battle room
replayid = replayid.slice(7);
if (Config.server.id !== 'showdown') {
if (!Config.server.registered) {
replayid = 'unregisteredserver-' + replayid;
} else {
replayid = Config.server.id + '-' + replayid;
}
}
} else {
// replay panel
replayid = room.fragment;
}
battle.fastForwardTo(-1);
let buf = '<!DOCTYPE html>\n';
buf += '<meta charset="utf-8" />\n';
buf += '<!-- version 1 -->\n';
buf += '<title>' + Tools.escapeHTML(battle.tier) + ' replay: ' + Tools.escapeHTML(battle.p1.name) + ' vs. ' + Tools.escapeHTML(battle.p2.name) + '</title>\n';
buf += '<style>\n';
buf += 'html,body {font-family:Verdana, sans-serif;font-size:10pt;margin:0;padding:0;}body{padding:12px 0;} .battle-log {font-family:Verdana, sans-serif;font-size:10pt;} .battle-log-inline {border:1px solid #AAAAAA;background:#EEF2F5;color:black;max-width:640px;margin:0 auto 80px;padding-bottom:5px;} .battle-log .inner {padding:4px 8px 0px 8px;} .battle-log .inner-preempt {padding:0 8px 4px 8px;} .battle-log .inner-after {margin-top:0.5em;} .battle-log h2 {margin:0.5em -8px;padding:4px 8px;border:1px solid #AAAAAA;background:#E0E7EA;border-left:0;border-right:0;font-family:Verdana, sans-serif;font-size:13pt;} .battle-log .chat {vertical-align:middle;padding:3px 0 3px 0;font-size:8pt;} .battle-log .chat strong {color:#40576A;} .battle-log .chat em {padding:1px 4px 1px 3px;color:#000000;font-style:normal;} .chat.mine {background:rgba(0,0,0,0.05);margin-left:-8px;margin-right:-8px;padding-left:8px;padding-right:8px;} .spoiler {color:#BBBBBB;background:#BBBBBB;padding:0px 3px;} .spoiler:hover, .spoiler:active, .spoiler-shown {color:#000000;background:#E2E2E2;padding:0px 3px;} .spoiler a {color:#BBBBBB;} .spoiler:hover a, .spoiler:active a, .spoiler-shown a {color:#2288CC;} .chat code, .chat .spoiler:hover code, .chat .spoiler:active code, .chat .spoiler-shown code {border:1px solid #C0C0C0;background:#EEEEEE;color:black;padding:0 2px;} .chat .spoiler code {border:1px solid #CCCCCC;background:#CCCCCC;color:#CCCCCC;} .battle-log .rated {padding:3px 4px;} .battle-log .rated strong {color:white;background:#89A;padding:1px 4px;border-radius:4px;} .spacer {margin-top:0.5em;} .message-announce {background:#6688AA;color:white;padding:1px 4px 2px;} .message-announce a, .broadcast-green a, .broadcast-blue a, .broadcast-red a {color:#DDEEFF;} .broadcast-green {background-color:#559955;color:white;padding:2px 4px;} .broadcast-blue {background-color:#6688AA;color:white;padding:2px 4px;} .infobox {border:1px solid #6688AA;padding:2px 4px;} .infobox-limited {max-height:200px;overflow:auto;overflow-x:hidden;} .broadcast-red {background-color:#AA5544;color:white;padding:2px 4px;} .message-learn-canlearn {font-weight:bold;color:#228822;text-decoration:underline;} .message-learn-cannotlearn {font-weight:bold;color:#CC2222;text-decoration:underline;} .message-effect-weak {font-weight:bold;color:#CC2222;} .message-effect-resist {font-weight:bold;color:#6688AA;} .message-effect-immune {font-weight:bold;color:#666666;} .message-learn-list {margin-top:0;margin-bottom:0;} .message-throttle-notice, .message-error {color:#992222;} .message-overflow, .chat small.message-overflow {font-size:0pt;} .message-overflow::before {font-size:9pt;content:\'...\';} .subtle {color:#3A4A66;}\n';
buf += '</style>\n';
buf += '<div class="wrapper replay-wrapper" style="max-width:1180px;margin:0 auto">\n';
buf += '<input type="hidden" name="replayid" value="' + replayid + '" />\n';
buf += '<div class="battle"></div><div class="battle-log"></div><div class="replay-controls"></div><div class="replay-controls-2"></div>\n';
buf += '<h1 style="font-weight:normal;text-align:center"><strong>' + Tools.escapeHTML(battle.tier) + '</strong><br /><a href="http://pokemonshowdown.com/users/' + toId(battle.p1.name) + '" class="subtle" target="_blank">' + Tools.escapeHTML(battle.p1.name) + '</a> vs. <a href="http://pokemonshowdown.com/users/' + toId(battle.p2.name) + '" class="subtle" target="_blank">' + Tools.escapeHTML(battle.p2.name) + '</a></h1>\n';
buf += '<script type="text/plain" class="battle-log-data">' + battle.activityQueue.join('\n').replace(/\//g, '\\/') + '</script>\n'; // lgtm [js/incomplete-sanitization]
buf += '</div>\n';
buf += '<div class="battle-log battle-log-inline"><div class="inner">' + battle.scene.$log.html() + '</div></div>\n';
buf += '</div>\n';
buf += '<script>\n';
buf += 'let daily = Math.floor(Date.now()/1000/60/60/24);document.write(\'<script src="https://play.pokemonshowdown.com/js/replay-embed.js?version\'+daily+\'"></\'+\'script>\');\n';
buf += '</script>\n';
return buf;
},
createReplayFileHref(room: any) {
// unescape(encodeURIComponent()) is necessary because btoa doesn't support Unicode
// @ts-ignore
return 'data:text/plain;base64,' + encodeURIComponent(btoa(unescape(encodeURIComponent(Tools.createReplayFile(room)))));
}
};

View File

@ -1,11 +1,7 @@
/**
* Pokemon Showdown Dex Misc
* Pokemon Showdown Log Misc
*
* Some miscellaneous helper functions for battle-dex.ts, namely:
*
* - polyfills, which in combination with Babel lets us write ES2018
* while targeting ES3 (we are in theory still IE7-compatible! I
* haven't tested in IE7 for years, though)
* Some miscellaneous helper functions for battle-log.ts, namely:
*
* - an MD5 hasher
*

486
src/battle-log.ts Normal file
View File

@ -0,0 +1,486 @@
/**
* Battle log
*
* @author Guangcong Luo <guangcongluo@gmail.com>
* @license MIT
*/
class BattleLog {
static escapeFormat(formatid: string): string {
let atIndex = formatid.indexOf('@@@');
if (atIndex >= 0) {
return this.escapeFormat(formatid.slice(0, atIndex)) + '<br />Custom rules: ' + this.escapeHTML(formatid.slice(atIndex + 3));
}
if (window.BattleFormats && BattleFormats[formatid]) {
return this.escapeHTML(BattleFormats[formatid].name);
}
return this.escapeHTML(formatid);
}
static escapeHTML(str: string, jsEscapeToo?: boolean) {
str = getString(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
if (jsEscapeToo) str = str.replace(/\\/g, '\\\\').replace(/'/g, '\\\'');
return str;
}
static unescapeHTML(str: string) {
str = (str ? '' + str : '');
return str.replace(/&quot;/g, '"').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
}
static colorCache = {} as {[userid: string]: string};
static hashColor(name: string) {
if (this.colorCache[name]) return this.colorCache[name];
let hash;
if (window.Config && Config.customcolors && Config.customcolors[name]) {
if (Config.customcolors[name].color) {
return (this.colorCache[name] = 'color:' + Config.customcolors[name].color + ';');
}
hash = MD5(Config.customcolors[name]);
} else {
hash = MD5(name);
}
let H = parseInt(hash.substr(4, 4), 16) % 360; // 0 to 360
let S = parseInt(hash.substr(0, 4), 16) % 50 + 40; // 40 to 89
let L = Math.floor(parseInt(hash.substr(8, 4), 16) % 20 + 30); // 30 to 49
let C = (100 - Math.abs(2 * L - 100)) * S / 100 / 100;
let X = C * (1 - Math.abs((H / 60) % 2 - 1));
let m = L / 100 - C / 2;
let R1, G1, B1;
switch (Math.floor(H / 60)) {
case 1: R1 = X; G1 = C; B1 = 0; break;
case 2: R1 = 0; G1 = C; B1 = X; break;
case 3: R1 = 0; G1 = X; B1 = C; break;
case 4: R1 = X; G1 = 0; B1 = C; break;
case 5: R1 = C; G1 = 0; B1 = X; break;
case 0: default: R1 = C; G1 = X; B1 = 0; break;
}
let R = R1 + m, G = G1 + m, B = B1 + m;
let lum = R * R * R * 0.2126 + G * G * G * 0.7152 + B * B * B * 0.0722; // 0.013 (dark blue) to 0.737 (yellow)
let HLmod = (lum - 0.2) * -150; // -80 (yellow) to 28 (dark blue)
if (HLmod > 18) HLmod = (HLmod - 18) * 2.5;
else if (HLmod < 0) HLmod = (HLmod - 0) / 3;
else HLmod = 0;
// let mod = ';border-right: ' + Math.abs(HLmod) + 'px solid ' + (HLmod > 0 ? 'red' : '#0088FF');
let Hdist = Math.min(Math.abs(180 - H), Math.abs(240 - H));
if (Hdist < 15) {
HLmod += (15 - Hdist) / 3;
}
L += HLmod;
this.colorCache[name] = "color:hsl(" + H + "," + S + "%," + L + "%);";
return this.colorCache[name];
}
static parseChatMessage(message: string, name: string, timestamp: string, isHighlighted?: boolean, $chatElem?: any) {
let showMe = !((Tools.prefs('chatformatting') || {}).hideme);
let group = ' ';
if (!/[A-Za-z0-9]/.test(name.charAt(0))) {
// Backwards compatibility
group = name.charAt(0);
name = name.substr(1);
}
let color = this.hashColor(toId(name));
let clickableName = '<small>' + this.escapeHTML(group) + '</small><span class="username" data-name="' + this.escapeHTML(name) + '">' + this.escapeHTML(name) + '</span>';
let hlClass = isHighlighted ? ' highlighted' : '';
let mineClass = (window.app && app.user && app.user.get('name') === name ? ' mine' : '');
let cmd = '';
let target = '';
if (message.charAt(0) === '/') {
if (message.charAt(1) === '/') {
message = message.slice(1);
} else {
let spaceIndex = message.indexOf(' ');
cmd = (spaceIndex >= 0 ? message.slice(1, spaceIndex) : message.slice(1));
if (spaceIndex >= 0) target = message.slice(spaceIndex + 1);
}
}
switch (cmd) {
case 'me':
if (!showMe) return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>/me' + this.parseMessage(' ' + target) + '</em></div>';
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">&bull;</strong> <em>' + clickableName + '<i>' + this.parseMessage(' ' + target) + '</i></em></div>';
case 'mee':
if (!showMe) return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>/me' + this.parseMessage(' ' + target).slice(1) + '</em></div>';
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">&bull;</strong> <em>' + clickableName + '<i>' + this.parseMessage(' ' + target).slice(1) + '</i></em></div>';
case 'invite':
let roomid = toRoomid(target);
return [
'<div class="chat">' + timestamp + '<em>' + clickableName + ' invited you to join the room "' + roomid + '"</em></div>',
'<div class="notice"><button name="joinRoom" value="' + roomid + '">Join ' + roomid + '</button></div>'
];
case 'announce':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <span class="message-announce">' + this.parseMessage(target) + '</span></div>';
case 'log':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<span class="message-log">' + this.parseMessage(target) + '</span></div>';
case 'data-pokemon':
let buf = '<li class="result">';
let template = Tools.getTemplate(target);
if (!template.abilities || !template.baseStats) return '[not supported in replays]';
buf += '<span class="col numcol">' + (template.tier || Tools.getTemplate(template.baseSpecies).tier) + '</span> ';
buf += '<span class="col iconcol"><span style="' + Tools.getPokemonIcon(template) + '"></span></span> ';
buf += '<span class="col pokemonnamecol" style="white-space:nowrap"><a href="https://pokemonshowdown.com/dex/pokemon/' + template.id + '" target="_blank">' + template.species + '</a></span> ';
buf += '<span class="col typecol">';
if (template.types) for (let i = 0; i < template.types.length; i++) {
buf += Tools.getTypeIcon(template.types[i]);
}
buf += '</span> ';
buf += '<span style="float:left;min-height:26px">';
if (template.abilities['1']) {
buf += '<span class="col twoabilitycol">' + template.abilities['0'] + '<br />' + template.abilities['1'] + '</span>';
} else {
buf += '<span class="col abilitycol">' + template.abilities['0'] + '</span>';
}
if (template.abilities['S']) {
buf += '<span class="col twoabilitycol' + (template.unreleasedHidden ? ' unreleasedhacol' : '') + '"><em>' + template.abilities['H'] + '<br />' + template.abilities['S'] + '</em></span>';
} else if (template.abilities['H']) {
buf += '<span class="col abilitycol' + (template.unreleasedHidden ? ' unreleasedhacol' : '') + '"><em>' + template.abilities['H'] + '</em></span>';
} else {
buf += '<span class="col abilitycol"></span>';
}
buf += '</span>';
buf += '<span style="float:left;min-height:26px">';
buf += '<span class="col statcol"><em>HP</em><br />' + template.baseStats.hp + '</span> ';
buf += '<span class="col statcol"><em>Atk</em><br />' + template.baseStats.atk + '</span> ';
buf += '<span class="col statcol"><em>Def</em><br />' + template.baseStats.def + '</span> ';
buf += '<span class="col statcol"><em>SpA</em><br />' + template.baseStats.spa + '</span> ';
buf += '<span class="col statcol"><em>SpD</em><br />' + template.baseStats.spd + '</span> ';
buf += '<span class="col statcol"><em>Spe</em><br />' + template.baseStats.spe + '</span> ';
let bst = 0;
for (const i in template.baseStats) bst += template.baseStats[i as StatName];
buf += '<span class="col bstcol"><em>BST<br />' + bst + '</em></span> ';
buf += '</span>';
buf += '</li>';
return '<div class="message"><ul class="utilichart">' + buf + '<li style=\"clear:both\"></li></ul></div>';
case 'data-item':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderItemRow(Tools.getItem(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'data-ability':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderAbilityRow(Tools.getAbility(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'data-move':
if (!window.BattleSearch) return '[not supported in replays]';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderMoveRow(Tools.getMove(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'text':
return '<div class="chat">' + this.parseMessage(target) + '</div>';
case 'error':
return '<div class="chat message-error">' + this.escapeHTML(target) + '</div>';
case 'html':
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + BattleLog.sanitizeHTML(target) + '</em></div>';
case 'uhtml':
case 'uhtmlchange':
let parts = target.split(',');
let $elements = $chatElem.find('div.uhtml-' + toId(parts[0]));
let html = parts.slice(1).join(',');
if (!html) {
$elements.remove();
} else if (!$elements.length) {
$chatElem.append('<div class="chat uhtml-' + toId(parts[0]) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
} else if (cmd === 'uhtmlchange') {
$elements.html(BattleLog.sanitizeHTML(html));
} else {
$elements.remove();
$chatElem.append('<div class="chat uhtml-' + toId(parts[0]) + '">' + BattleLog.sanitizeHTML(html) + '</div>');
}
return '';
case 'raw':
return '<div class="chat">' + BattleLog.sanitizeHTML(target) + '</div>';
default:
// Not a command or unsupported. Parsed as a normal chat message.
if (!name) {
return '<div class="chat' + hlClass + '">' + timestamp + '<em>' + this.parseMessage(message) + '</em></div>';
}
return '<div class="chat chatmessage-' + toId(name) + hlClass + mineClass + '">' + timestamp + '<strong style="' + color + '">' + clickableName + ':</strong> <em>' + this.parseMessage(message) + '</em></div>';
}
}
static parseMessage(str: string) {
// Don't format console commands (>>).
if (str.substr(0, 3) === '>> ' || str.substr(0, 4) === '>>> ') return this.escapeHTML(str);
// Don't format console results (<<).
if (str.substr(0, 3) === '<< ') return this.escapeHTML(str);
str = formatText(str);
let options = Tools.prefs('chatformatting') || {};
if (options.hidelinks) {
str = str.replace(/<a[^>]*>/g, '<u>').replace(/<\/a>/g, '</u>');
}
if (options.hidespoiler) {
str = str.replace(/<span class="spoiler">/g, '<span class="spoiler spoiler-shown">');
}
if (options.hidegreentext) {
str = str.replace(/<span class="greentext">/g, '<span>');
}
return str;
}
static interstice = (() => {
const whitelist: string[] = (window.Config && Config.whitelist) ? Config.whitelist : [];
const patterns = whitelist.map(entry => new RegExp(
`^(https?:)?//([A-Za-z0-9-]*\\.)?${entry}(/.*)?`,
'i'));
return {
isWhitelisted(uri: string) {
if (uri[0] === '/' && uri[1] !== '/') {
// domain-relative URIs are safe
return true;
}
for (const pattern of patterns) {
if (pattern.test(uri)) return true;
}
return false;
},
getURI(uri: string) {
return 'http://pokemonshowdown.com/interstice?uri=' + encodeURIComponent(uri);
},
};
})();
static tagPolicy: (tagName: string, attribs: string[]) => any = null!;
static initSanitizeHTML() {
if (this.tagPolicy) return;
if (!('html4' in window)) {
throw new Error('sanitizeHTML requires caja');
}
// Add <marquee> <blink> <psicon> to the whitelist.
Object.assign(html4.ELEMENTS, {
'marquee': 0,
'blink': 0,
'psicon': html4.eflags['OPTIONAL_ENDTAG'] | html4.eflags['EMPTY']
});
Object.assign(html4.ATTRIBS, {
// See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/marquee
'marquee::behavior': 0,
'marquee::bgcolor': 0,
'marquee::direction': 0,
'marquee::height': 0,
'marquee::hspace': 0,
'marquee::loop': 0,
'marquee::scrollamount': 0,
'marquee::scrolldelay': 0,
'marquee::truespeed': 0,
'marquee::vspace': 0,
'marquee::width': 0,
'psicon::pokemon': 0,
'psicon::item': 0
});
this.tagPolicy = (tagName: string, attribs: string[]) => {
if (html4.ELEMENTS[tagName] & html4.eflags['UNSAFE']) {
return;
}
let targetIdx = 0, srcIdx = 0;
if (tagName === 'a') {
// Special handling of <a> tags.
for (let i = 0; i < attribs.length - 1; i += 2) {
switch (attribs[i]) {
case 'target':
targetIdx = i + 1;
break;
}
}
}
let dataUri = '';
if (tagName === 'img') {
for (let i = 0; i < attribs.length - 1; i += 2) {
if (attribs[i] === 'src' && attribs[i + 1].substr(0, 11) === 'data:image/') {
srcIdx = i;
dataUri = attribs[i + 1];
}
if (attribs[i] === 'src' && attribs[i + 1].substr(0, 2) === '//') {
if (location.protocol !== 'http:' && location.protocol !== 'https:') {
attribs[i + 1] = 'http:' + attribs[i + 1];
}
}
}
} else if (tagName === 'psicon') {
// <psicon> is a custom element which supports a set of mutually incompatible attributes:
// <psicon pokemon> and <psicon item>
let classValueIndex = -1;
let styleValueIndex = -1;
let iconAttrib = null;
for (let i = 0; i < attribs.length - 1; i += 2) {
if (attribs[i] === 'pokemon' || attribs[i] === 'item') {
// If declared more than once, use the later.
iconAttrib = attribs.slice(i, i + 2);
} else if (attribs[i] === 'class') {
classValueIndex = i + 1;
} else if (attribs[i] === 'style') {
styleValueIndex = i + 1;
}
}
tagName = 'span';
if (iconAttrib) {
if (classValueIndex < 0) {
attribs.push('class', '');
classValueIndex = attribs.length - 1;
}
if (styleValueIndex < 0) {
attribs.push('style', '');
styleValueIndex = attribs.length - 1;
}
// Prepend all the classes and styles associated to the custom element.
if (iconAttrib[0] === 'pokemon') {
attribs[classValueIndex] = attribs[classValueIndex] ? 'picon ' + attribs[classValueIndex] : 'picon';
attribs[styleValueIndex] = attribs[styleValueIndex] ? Tools.getPokemonIcon(iconAttrib[1]) + '; ' + attribs[styleValueIndex] : Tools.getPokemonIcon(iconAttrib[1]);
} else if (iconAttrib[0] === 'item') {
attribs[classValueIndex] = attribs[classValueIndex] ? 'itemicon ' + attribs[classValueIndex] : 'itemicon';
attribs[styleValueIndex] = attribs[styleValueIndex] ? Tools.getItemIcon(iconAttrib[1]) + '; ' + attribs[styleValueIndex] : Tools.getItemIcon(iconAttrib[1]);
}
}
}
if (attribs[targetIdx] === 'replace') {
targetIdx = -targetIdx;
}
attribs = html.sanitizeAttribs(tagName, attribs, (urlData: any) => {
if (urlData.scheme_ === 'geo' || urlData.scheme_ === 'sms' || urlData.scheme_ === 'tel') return null;
return urlData;
});
if (targetIdx < 0) {
targetIdx = -targetIdx;
attribs[targetIdx - 1] = 'data-target';
attribs[targetIdx] = 'replace';
targetIdx = 0;
}
if (dataUri && tagName === 'img') {
attribs[srcIdx + 1] = dataUri;
}
if (tagName === 'a' || tagName === 'form') {
if (targetIdx) {
attribs[targetIdx] = '_blank';
} else {
attribs.push('target');
attribs.push('_blank');
}
if (tagName === 'a') {
attribs.push('rel');
attribs.push('noopener');
}
}
return {tagName: tagName, attribs: attribs};
};
}
static localizeTime(full: string, date: string, time: string, timezone?: string) {
let parsedTime = new Date(date + 'T' + time + (timezone || 'Z').toUpperCase());
// Very old (pre-ES5) web browsers may be incapable of parsing ISO 8601
// dates. In such a case, gracefully continue without replacing the date
// format.
if (!parsedTime.getTime()) return full;
let formattedTime;
// Try using Intl API if it exists
if (window.Intl && Intl.DateTimeFormat) {
formattedTime = new Intl.DateTimeFormat(undefined, {month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric'}).format(parsedTime);
} else {
// toLocaleString even exists in ECMAScript 1, so no need to check
// if it exists.
formattedTime = parsedTime.toLocaleString();
}
return '<time>' + this.escapeHTML(formattedTime) + '</time>';
}
static sanitizeHTML(input: string) {
this.initSanitizeHTML();
const sanitized = html.sanitizeWithPolicy(getString(input), this.tagPolicy) as string;
// <time> parsing requires ISO 8601 time. While more time formats are
// supported by most JavaScript implementations, it isn't required, and
// how to exactly enforce ignoring user agent timezone setting is not obvious.
// As dates come from the server which isn't aware of client timezone, a
// particular timezone is required.
//
// This regular expression is split into three groups.
//
// Group 1 - date
// Group 2 - time (seconds and milliseconds are optional)
// Group 3 - optional timezone
//
// Group 1 and group 2 are split to allow using space as a separator
// instead of T. Stricly speaking ECMAScript 5 specification only
// allows T, however it's more practical to also allow spaces.
return sanitized.replace(
/<time>\s*([+-]?\d{4,}-\d{2}-\d{2})[T ](\d{2}:\d{2}(?::\d{2}(?:\.\d{3})?)?)(Z|[+-]\d{2}:\d{2})?\s*<\/time>/ig,
this.localizeTime);
}
/*********************************************************
* Replay files
*********************************************************/
// Replay files are .html files that display a replay for a battle.
// The .html files mainly contain replay log data; the actual replay
// player is downloaded online. Also included is a textual log and
// some minimal CSS to make it look pretty, for offline viewing.
// This strategy helps keep the replay file reasonably small; of
// the 30 KB or so for a 50-turn battle, around 10 KB is the log
// data, and around 20 KB is the textual log.
// The actual replay player is downloaded from replay-embed.js,
// which handles loading all the necessary resources for turning the log
// data into a playable replay.
// Battle log data is stored in and loaded from a
// <script type="text/plain" class="battle-log-data"> tag.
// replay-embed.js is loaded through a cache-buster that rotates daily.
// This allows pretty much anything about the replay viewer to be
// updated as desired.
static createReplayFile(room: any) {
let battle = room.battle;
let replayid = room.id;
if (replayid) {
// battle room
replayid = replayid.slice(7);
if (Config.server.id !== 'showdown') {
if (!Config.server.registered) {
replayid = 'unregisteredserver-' + replayid;
} else {
replayid = Config.server.id + '-' + replayid;
}
}
} else {
// replay panel
replayid = room.fragment;
}
battle.fastForwardTo(-1);
let buf = '<!DOCTYPE html>\n';
buf += '<meta charset="utf-8" />\n';
buf += '<!-- version 1 -->\n';
buf += '<title>' + this.escapeHTML(battle.tier) + ' replay: ' + this.escapeHTML(battle.p1.name) + ' vs. ' + this.escapeHTML(battle.p2.name) + '</title>\n';
buf += '<style>\n';
buf += 'html,body {font-family:Verdana, sans-serif;font-size:10pt;margin:0;padding:0;}body{padding:12px 0;} .battle-log {font-family:Verdana, sans-serif;font-size:10pt;} .battle-log-inline {border:1px solid #AAAAAA;background:#EEF2F5;color:black;max-width:640px;margin:0 auto 80px;padding-bottom:5px;} .battle-log .inner {padding:4px 8px 0px 8px;} .battle-log .inner-preempt {padding:0 8px 4px 8px;} .battle-log .inner-after {margin-top:0.5em;} .battle-log h2 {margin:0.5em -8px;padding:4px 8px;border:1px solid #AAAAAA;background:#E0E7EA;border-left:0;border-right:0;font-family:Verdana, sans-serif;font-size:13pt;} .battle-log .chat {vertical-align:middle;padding:3px 0 3px 0;font-size:8pt;} .battle-log .chat strong {color:#40576A;} .battle-log .chat em {padding:1px 4px 1px 3px;color:#000000;font-style:normal;} .chat.mine {background:rgba(0,0,0,0.05);margin-left:-8px;margin-right:-8px;padding-left:8px;padding-right:8px;} .spoiler {color:#BBBBBB;background:#BBBBBB;padding:0px 3px;} .spoiler:hover, .spoiler:active, .spoiler-shown {color:#000000;background:#E2E2E2;padding:0px 3px;} .spoiler a {color:#BBBBBB;} .spoiler:hover a, .spoiler:active a, .spoiler-shown a {color:#2288CC;} .chat code, .chat .spoiler:hover code, .chat .spoiler:active code, .chat .spoiler-shown code {border:1px solid #C0C0C0;background:#EEEEEE;color:black;padding:0 2px;} .chat .spoiler code {border:1px solid #CCCCCC;background:#CCCCCC;color:#CCCCCC;} .battle-log .rated {padding:3px 4px;} .battle-log .rated strong {color:white;background:#89A;padding:1px 4px;border-radius:4px;} .spacer {margin-top:0.5em;} .message-announce {background:#6688AA;color:white;padding:1px 4px 2px;} .message-announce a, .broadcast-green a, .broadcast-blue a, .broadcast-red a {color:#DDEEFF;} .broadcast-green {background-color:#559955;color:white;padding:2px 4px;} .broadcast-blue {background-color:#6688AA;color:white;padding:2px 4px;} .infobox {border:1px solid #6688AA;padding:2px 4px;} .infobox-limited {max-height:200px;overflow:auto;overflow-x:hidden;} .broadcast-red {background-color:#AA5544;color:white;padding:2px 4px;} .message-learn-canlearn {font-weight:bold;color:#228822;text-decoration:underline;} .message-learn-cannotlearn {font-weight:bold;color:#CC2222;text-decoration:underline;} .message-effect-weak {font-weight:bold;color:#CC2222;} .message-effect-resist {font-weight:bold;color:#6688AA;} .message-effect-immune {font-weight:bold;color:#666666;} .message-learn-list {margin-top:0;margin-bottom:0;} .message-throttle-notice, .message-error {color:#992222;} .message-overflow, .chat small.message-overflow {font-size:0pt;} .message-overflow::before {font-size:9pt;content:\'...\';} .subtle {color:#3A4A66;}\n';
buf += '</style>\n';
buf += '<div class="wrapper replay-wrapper" style="max-width:1180px;margin:0 auto">\n';
buf += '<input type="hidden" name="replayid" value="' + replayid + '" />\n';
buf += '<div class="battle"></div><div class="battle-log"></div><div class="replay-controls"></div><div class="replay-controls-2"></div>\n';
buf += '<h1 style="font-weight:normal;text-align:center"><strong>' + this.escapeHTML(battle.tier) + '</strong><br /><a href="http://pokemonshowdown.com/users/' + toId(battle.p1.name) + '" class="subtle" target="_blank">' + this.escapeHTML(battle.p1.name) + '</a> vs. <a href="http://pokemonshowdown.com/users/' + toId(battle.p2.name) + '" class="subtle" target="_blank">' + this.escapeHTML(battle.p2.name) + '</a></h1>\n';
buf += '<script type="text/plain" class="battle-log-data">' + battle.activityQueue.join('\n').replace(/\//g, '\\/') + '</script>\n'; // lgtm [js/incomplete-sanitization]
buf += '</div>\n';
buf += '<div class="battle-log battle-log-inline"><div class="inner">' + battle.scene.$log.html() + '</div></div>\n';
buf += '</div>\n';
buf += '<script>\n';
buf += 'let daily = Math.floor(Date.now()/1000/60/60/24);document.write(\'<script src="https://play.pokemonshowdown.com/js/replay-embed.js?version\'+daily+\'"></\'+\'script>\');\n';
buf += '</script>\n';
return buf;
}
static createReplayFileHref(room: any) {
// unescape(encodeURIComponent()) is necessary because btoa doesn't support Unicode
// @ts-ignore
return 'data:text/plain;base64,' + encodeURIComponent(btoa(unescape(encodeURIComponent(this.createReplayFile(room)))));
}
}

View File

@ -332,7 +332,7 @@ class Pokemon {
}
}
htmlName() {
return '<span class="battle-nickname' + (this.side.n === 0 ? '' : '-foe') + '" title="' + this.species + '">' + Tools.escapeHTML(this.name) + '</span>';
return '<span class="battle-nickname' + (this.side.n === 0 ? '' : '-foe') + '" title="' + this.species + '">' + BattleLog.escapeHTML(this.name) + '</span>';
}
getName(shortName?: boolean) {
if (this.side.n === 0) {
@ -358,7 +358,7 @@ class Pokemon {
return titlestring;
}
getFullName(plaintext?: boolean) {
let name = this.side && this.side.n && (this.side.battle.ignoreOpponent || this.side.battle.ignoreNicks) ? this.species : Tools.escapeHTML(this.name);
let name = this.side && this.side.n && (this.side.battle.ignoreOpponent || this.side.battle.ignoreNicks) ? this.species : BattleLog.escapeHTML(this.name);
if (name !== this.species) {
if (plaintext) {
name += ' (' + this.species + ')';
@ -787,7 +787,7 @@ class Side {
if (pokemon.side.n === 0) {
this.battle.message('Go! ' + pokemon.getFullName() + '!');
} else {
this.battle.message('' + Tools.escapeHTML(pokemon.side.name) + ' sent out ' + pokemon.getFullName() + '!');
this.battle.message('' + BattleLog.escapeHTML(pokemon.side.name) + ' sent out ' + pokemon.getFullName() + '!');
}
this.battle.scene.animSummon(pokemon, slot);
@ -849,12 +849,12 @@ class Side {
pokemon.removeVolatile('formechange' as ID);
}
if (pokemon.lastMove === 'uturn' || pokemon.lastMove === 'voltswitch') {
this.battle.message('' + pokemon.getName() + ' went back to ' + Tools.escapeHTML(pokemon.side.name) + '!');
this.battle.message('' + pokemon.getName() + ' went back to ' + BattleLog.escapeHTML(pokemon.side.name) + '!');
} else if (pokemon.lastMove !== 'batonpass' && pokemon.lastMove !== 'zbatonpass') {
if (pokemon.side.n === 0) {
this.battle.message('' + pokemon.getName() + ', come back!');
} else {
this.battle.message('' + Tools.escapeHTML(pokemon.side.name) + ' withdrew ' + pokemon.getFullName() + '!');
this.battle.message('' + BattleLog.escapeHTML(pokemon.side.name) + ' withdrew ' + pokemon.getFullName() + '!');
}
}
pokemon.statusData.toxicTurns = 0;
@ -1163,12 +1163,12 @@ class Battle {
// activities
//
start() {
this.scene.log('<div>Battle between ' + Tools.escapeHTML(this.p1.name) + ' and ' + Tools.escapeHTML(this.p2.name) + ' started!</div>');
this.scene.log('<div>Battle between ' + BattleLog.escapeHTML(this.p1.name) + ' and ' + BattleLog.escapeHTML(this.p2.name) + ' started!</div>');
if (this.startCallback) this.startCallback(this);
}
winner(winner?: string) {
if (winner) this.message('' + Tools.escapeHTML(winner) + ' won the battle!');
else this.message('Tie between ' + Tools.escapeHTML(this.p1.name) + ' and ' + Tools.escapeHTML(this.p2.name) + '!');
if (winner) this.message('' + BattleLog.escapeHTML(winner) + ' won the battle!');
else this.message('Tie between ' + BattleLog.escapeHTML(this.p1.name) + ' and ' + BattleLog.escapeHTML(this.p2.name) + '!');
this.ended = true;
}
prematureEnd() {
@ -1440,7 +1440,7 @@ class Battle {
// for (let i = 0; i < 10; i++) {
// let name = people[Math.floor(Math.random() * people.length)];
// if (!button) button = buttons[Math.floor(Math.random() * buttons.length)];
// this.scene.log('<div class="chat"><strong style="' + hashColor(toUserid(name)) + '" class="username" data-name="' + Tools.escapeHTML(name) + '">' + Tools.escapeHTML(name) + ':</strong> <em>' + button + '</em></div>');
// this.scene.log('<div class="chat"><strong style="' + BattleLog.hashColor(toUserid(name)) + '" class="username" data-name="' + BattleLog.escapeHTML(name) + '">' + BattleLog.escapeHTML(name) + ':</strong> <em>' + button + '</em></div>');
// button = (name === 'Diatom' ? "thanks diatom" : null);
// }
} else {
@ -2268,7 +2268,7 @@ class Battle {
actions += '' + ofpoke!.getName() + ' surrounded itself with a veil of petals!';
break;
default:
let stat = Tools.escapeHTML(args[3]);
let stat = BattleLog.escapeHTML(args[3]);
actions += "" + poke.getName() + "'s " + (stat ? stat + " was" : "stats were") + " not lowered!";
}
break;
@ -2899,10 +2899,10 @@ class Battle {
let poke = this.getPokemon(args[1])!;
let item = Tools.getItem(args[3]);
if (args[2] === 'Rayquaza') {
actions += "" + Tools.escapeHTML(poke.side.name) + "'s fervent wish has reached " + poke.getLowerName() + "!";
actions += "" + BattleLog.escapeHTML(poke.side.name) + "'s fervent wish has reached " + poke.getLowerName() + "!";
} else {
poke.item = item.name;
actions += "" + poke.getName() + "'s " + item.name + " is reacting to " + (this.gen >= 7 ? "the Key Stone" : Tools.escapeHTML(poke.side.name) + "'s Mega Bracelet") + "!";
actions += "" + poke.getName() + "'s " + item.name + " is reacting to " + (this.gen >= 7 ? "the Key Stone" : BattleLog.escapeHTML(poke.side.name) + "'s Mega Bracelet") + "!";
}
actions += "<br />" + poke.getName() + " has Mega Evolved into Mega " + args[2] + "!";
break;
@ -2932,7 +2932,7 @@ class Battle {
} else {
switch (effect.id) {
case 'typechange':
const types = Tools.escapeHTML(args[3]);
const types = BattleLog.escapeHTML(args[3]);
poke.removeVolatile('typeadd' as ID);
poke.addVolatile('typechange' as ID, types);
if (kwargs.silent) {
@ -2962,7 +2962,7 @@ class Battle {
}).join(' '), 'neutral');
break;
case 'typeadd':
const type = Tools.escapeHTML(args[3]);
const type = BattleLog.escapeHTML(args[3]);
poke.addVolatile('typeadd' as ID, type);
if (kwargs.silent) break;
actions += "" + type + " type was added to " + poke.getLowerName() + "!";
@ -3032,7 +3032,7 @@ class Battle {
ofpoke!.markAbility(fromeffect.name);
}
this.scene.resultAnim(poke, 'Disabled', 'bad');
actions += "" + poke.getName() + "'s " + Tools.escapeHTML(args[3]) + " was disabled!";
actions += "" + poke.getName() + "'s " + BattleLog.escapeHTML(args[3]) + " was disabled!";
break;
case 'embargo':
this.scene.resultAnim(poke, 'Embargo', 'bad');
@ -3166,7 +3166,7 @@ class Battle {
actions += '' + poke.getName() + ' foresaw an attack!';
break;
case 'mimic':
actions += '' + poke.getName() + ' learned ' + Tools.escapeHTML(args[3]) + '!';
actions += '' + poke.getName() + ' learned ' + BattleLog.escapeHTML(args[3]) + '!';
break;
case 'laserfocus':
actions += '' + poke.getName() + ' concentrated intensely!';
@ -3455,7 +3455,7 @@ class Battle {
actions += "" + poke.getName() + " snatched " + ofpoke!.getLowerName() + "'s move!";
break;
case 'grudge':
actions += "" + poke.getName() + "'s " + Tools.escapeHTML(args[3]) + " lost all of its PP due to the grudge!";
actions += "" + poke.getName() + "'s " + BattleLog.escapeHTML(args[3]) + " lost all of its PP due to the grudge!";
poke.markMove(args[3], Infinity);
break;
case 'quickguard':
@ -3512,7 +3512,7 @@ class Battle {
actions += 'Everyone is caught up in the happy atmosphere!';
break;
case 'celebrate':
actions += 'Congratulations, ' + Tools.escapeHTML(poke.side.name) + '!';
actions += 'Congratulations, ' + BattleLog.escapeHTML(poke.side.name) + '!';
break;
// move activations
@ -3534,7 +3534,7 @@ class Battle {
ofpoke!.side.removeSideCondition('LightScreen');
break;
case 'beatup':
actions += "" + Tools.escapeHTML(kwargs.of) + "'s attack!";
actions += "" + BattleLog.escapeHTML(kwargs.of) + "'s attack!";
break;
case 'pursuit':
actions += "(" + poke.getName() + " is being withdrawn!)";
@ -3561,7 +3561,7 @@ class Battle {
break;
case 'spite':
let move = Tools.getMove(args[3]).name;
let pp = Tools.escapeHTML(args[4]);
let pp = BattleLog.escapeHTML(args[4]);
actions += "It reduced the PP of " + poke.getLowerName() + "'s " + move + " by " + pp + "!";
poke.markMove(move, Number(pp));
break;
@ -3572,16 +3572,16 @@ class Battle {
this.scene.anim(poke, {time: 100});
break;
case 'magnitude':
actions += "Magnitude " + Tools.escapeHTML(args[3]) + "!";
actions += "Magnitude " + BattleLog.escapeHTML(args[3]) + "!";
break;
case 'sketch':
actions += "" + poke.getName() + " sketched " + Tools.escapeHTML(args[3]) + "!";
actions += "" + poke.getName() + " sketched " + BattleLog.escapeHTML(args[3]) + "!";
break;
case 'skillswap':
actions += "" + poke.getName() + " swapped Abilities with its target!";
if (this.gen <= 4) break;
let pokeability = Tools.escapeHTML(args[3]) || ofpoke!.ability;
let ofpokeability = Tools.escapeHTML(args[4]) || poke.ability;
let pokeability = BattleLog.escapeHTML(args[3]) || ofpoke!.ability;
let ofpokeability = BattleLog.escapeHTML(args[4]) || poke.ability;
if (pokeability) {
poke.ability = pokeability;
if (!ofpoke!.baseAbility) ofpoke!.baseAbility = pokeability;
@ -3646,7 +3646,7 @@ class Battle {
actions += '' + poke.getName() + ' anchored itself with its roots!';
break;
case 'matblock':
actions += '' + Tools.escapeHTML(args[3]) + ' was blocked by the kicked-up mat!';
actions += '' + BattleLog.escapeHTML(args[3]) + ' was blocked by the kicked-up mat!';
break;
case 'powder':
actions += 'When the flame touched the powder on the Pokémon, it exploded!';
@ -3682,10 +3682,10 @@ class Battle {
break;
case 'forewarn':
if (this.gen >= 5) {
actions += "It was alerted to " + ofpoke!.getLowerName() + "'s " + Tools.escapeHTML(args[3]) + "!";
actions += "It was alerted to " + ofpoke!.getLowerName() + "'s " + BattleLog.escapeHTML(args[3]) + "!";
ofpoke!.markMove(args[3], 0);
} else {
actions += "" + poke.getName() + "'s Forewarn alerted it to " + Tools.escapeHTML(args[3]) + "!";
actions += "" + poke.getName() + "'s Forewarn alerted it to " + BattleLog.escapeHTML(args[3]) + "!";
let foeActive = [] as Pokemon[];
for (const target of poke.side.foe.active) if (target) foeActive.push(target);
if (foeActive.length === 1) {
@ -3759,7 +3759,7 @@ class Battle {
break;
case 'leppaberry':
case 'mysteryberry':
actions += '' + poke.getName() + " restored PP to its " + Tools.escapeHTML(args[3]) + " move using " + effect.name + "!";
actions += '' + poke.getName() + " restored PP to its " + BattleLog.escapeHTML(args[3]) + " move using " + effect.name + "!";
poke.markMove(args[3], effect.id === 'leppaberry' ? -10 : -5);
break;
case 'focusband':
@ -3768,7 +3768,7 @@ class Battle {
break;
case 'safetygoggles':
poke.item = 'Safety Goggles';
actions += '' + poke.getName() + " is not affected by " + Tools.escapeHTML(args[3]) + " thanks to its Safety Goggles!";
actions += '' + poke.getName() + " is not affected by " + BattleLog.escapeHTML(args[3]) + " thanks to its Safety Goggles!";
break;
case 'protectivepads':
poke.item = 'Protective Pads';
@ -4037,7 +4037,7 @@ class Battle {
break;
} case '-message': {
actions += Tools.escapeHTML(args[1]);
actions += BattleLog.escapeHTML(args[1]);
break;
} case '-anim': {
@ -4052,7 +4052,7 @@ class Battle {
break;
} case '-hint': {
this.message('', '<small>(' + Tools.escapeHTML(args[1]) + ')</small>');
this.message('', '<small>(' + BattleLog.escapeHTML(args[1]) + ')</small>');
break;
} default: {
@ -4371,7 +4371,7 @@ class Battle {
} case 'tier': {
if (!args[1]) args[1] = '';
for (let i in kwargs) args[1] += '[' + i + '] ' + kwargs[i];
this.scene.log('<div style="padding:5px 0"><small>Format:</small> <br /><strong>' + Tools.escapeHTML(args[1]) + '</strong></div>');
this.scene.log('<div style="padding:5px 0"><small>Format:</small> <br /><strong>' + BattleLog.escapeHTML(args[1]) + '</strong></div>');
this.tier = args[1];
if (this.tier.slice(-13) === 'Random Battle') {
this.speciesClause = true;
@ -4397,16 +4397,16 @@ class Battle {
this.scene.updateGen();
break;
} case 'variation': {
this.scene.log('<div><small>Variation: <em>' + Tools.escapeHTML(args[1]) + '</em></small></div>');
this.scene.log('<div><small>Variation: <em>' + BattleLog.escapeHTML(args[1]) + '</em></small></div>');
break;
} case 'rule': {
let ruleArgs = args[1].split(': ');
this.scene.log('<div><small><em>' + Tools.escapeHTML(ruleArgs[0]) + (ruleArgs[1] ? ':' : '') + '</em> ' + Tools.escapeHTML(ruleArgs[1] || '') + '</div>');
this.scene.log('<div><small><em>' + BattleLog.escapeHTML(ruleArgs[0]) + (ruleArgs[1] ? ':' : '') + '</em> ' + BattleLog.escapeHTML(ruleArgs[1] || '') + '</div>');
if (ruleArgs[0] === 'Species Clause') this.speciesClause = true;
break;
} case 'rated': {
this.rated = true;
this.scene.log('<div class="rated"><strong>' + (Tools.escapeHTML(args[1]) || 'Rated battle') + '</strong></div>');
this.scene.log('<div class="rated"><strong>' + (BattleLog.escapeHTML(args[1]) || 'Rated battle') + '</strong></div>');
break;
} case ':': {
break;
@ -4423,7 +4423,7 @@ class Battle {
if (window.app && app.ignore && app.ignore[toUserid(name)] && (rank === ' ' || rank === '+' || rank === '\u2605' || rank === '\u2606')) break;
let message = args[1].slice(pipeIndex + 1);
let isHighlighted = window.app && app.rooms && app.rooms[this.roomid].getHighlight(message);
let parsedMessage = Tools.parseChatMessage(message, name, '', isHighlighted);
let parsedMessage = BattleLog.parseChatMessage(message, name, '', isHighlighted);
if (!Array.isArray(parsedMessage)) parsedMessage = [parsedMessage];
for (let i = 0; i < parsedMessage.length; i++) {
if (!parsedMessage[i]) continue;
@ -4435,19 +4435,19 @@ class Battle {
}
break;
} case 'chatmsg': {
this.scene.log('<div class="chat">' + Tools.escapeHTML(args[1]) + '</div>', preempt);
this.scene.log('<div class="chat">' + BattleLog.escapeHTML(args[1]) + '</div>', preempt);
break;
} case 'chatmsg-raw': case 'raw': case 'html': {
this.scene.log('<div class="chat">' + Tools.sanitizeHTML(args[1]) + '</div>', preempt);
this.scene.log('<div class="chat">' + BattleLog.sanitizeHTML(args[1]) + '</div>', preempt);
break;
} case 'error': {
this.scene.log('<div class="chat message-error">' + Tools.escapeHTML(args[1]) + '</div>', preempt);
this.scene.log('<div class="chat message-error">' + BattleLog.escapeHTML(args[1]) + '</div>', preempt);
break;
} case 'pm': {
this.scene.log('<div class="chat"><strong>' + Tools.escapeHTML(args[1]) + ':</strong> <span class="message-pm"><i style="cursor:pointer" onclick="selectTab(\'lobby\');rooms.lobby.popupOpen(\'' + Tools.escapeHTML(args[2], true) + '\')">(Private to ' + Tools.escapeHTML(args[3]) + ')</i> ' + Tools.parseMessage(args[4]) + '</span>');
this.scene.log('<div class="chat"><strong>' + BattleLog.escapeHTML(args[1]) + ':</strong> <span class="message-pm"><i style="cursor:pointer" onclick="selectTab(\'lobby\');rooms.lobby.popupOpen(\'' + BattleLog.escapeHTML(args[2], true) + '\')">(Private to ' + BattleLog.escapeHTML(args[3]) + ')</i> ' + BattleLog.parseMessage(args[4]) + '</span>');
break;
} case 'askreg': {
this.scene.log('<div class="broadcast-blue"><b>Register an account to protect your ladder rating!</b><br /><button name="register" value="' + Tools.escapeHTML(args[1]) + '"><b>Register</b></button></div>');
this.scene.log('<div class="broadcast-blue"><b>Register an account to protect your ladder rating!</b><br /><button name="register" value="' + BattleLog.escapeHTML(args[1]) + '"><b>Register</b></button></div>');
break;
} case 'inactive': {
if (!this.kickingInactive) this.kickingInactive = true;
@ -4469,11 +4469,11 @@ class Battle {
this.kickingInactive = parseInt(args[1].slice(hasIndex + 5), 10) || true;
}
}
this.scene.log('<div class="chat message-error">' + Tools.escapeHTML(args[1]) + '</div>', preempt);
this.scene.log('<div class="chat message-error">' + BattleLog.escapeHTML(args[1]) + '</div>', preempt);
break;
} case 'inactiveoff': {
this.kickingInactive = false;
this.scene.log('<div class="chat message-error">' + Tools.escapeHTML(args[1]) + '</div>', preempt);
this.scene.log('<div class="chat message-error">' + BattleLog.escapeHTML(args[1]) + '</div>', preempt);
break;
} case 'timer': {
break;
@ -4490,7 +4490,7 @@ class Battle {
room.userList.updateNoUsersOnline();
}
if (!this.ignoreSpects) {
this.scene.log('<div class="chat"><small>' + Tools.escapeHTML(args[1]) + ' joined.</small></div>', preempt);
this.scene.log('<div class="chat"><small>' + BattleLog.escapeHTML(args[1]) + ' joined.</small></div>', preempt);
}
break;
} case 'leave': case 'l': {
@ -4505,7 +4505,7 @@ class Battle {
room.userList.updateNoUsersOnline();
}
if (!this.ignoreSpects) {
this.scene.log('<div class="chat"><small>' + Tools.escapeHTML(args[1]) + ' left.</small></div>', preempt);
this.scene.log('<div class="chat"><small>' + BattleLog.escapeHTML(args[1]) + ' left.</small></div>', preempt);
}
break;
} case 'J': case 'L': case 'N': case 'n': case 'spectator': case 'spectatorleave': {
@ -4637,16 +4637,16 @@ class Battle {
this.cantUseMove(poke, effect, move, kwargs);
break;
} case 'message': {
this.message(Tools.escapeHTML(args[1]));
this.message(BattleLog.escapeHTML(args[1]));
break;
} case 'bigerror': {
this.message('<div class="broadcast-red">' + Tools.escapeHTML(args[1]).replace(/\|/g, '<br />') + '</div>');
this.message('<div class="broadcast-red">' + BattleLog.escapeHTML(args[1]).replace(/\|/g, '<br />') + '</div>');
break;
} case 'done': case '': {
if (this.ended || this.endPrevAction()) return;
break;
} case 'warning': {
this.message('<strong>Warning:</strong> ' + Tools.escapeHTML(args[1]));
this.message('<strong>Warning:</strong> ' + BattleLog.escapeHTML(args[1]));
this.message('Bug? Report it to <a href="http://www.smogon.com/forums/showthread.php?t=3453192">the replay viewer\'s Smogon thread</a>');
this.scene.wait(1000);
break;
@ -4661,7 +4661,7 @@ class Battle {
} case 'debug': {
args.shift();
const name = args.join(' ');
this.scene.log('<div class="debug"><div class="chat"><small style="color:#999">[DEBUG] ' + Tools.escapeHTML(name) + '.</small></div></div>', preempt);
this.scene.log('<div class="debug"><div class="chat"><small style="color:#999">[DEBUG] ' + BattleLog.escapeHTML(name) + '.</small></div></div>', preempt);
break;
} case 'seed': case 'choice': {
break;
@ -4671,13 +4671,13 @@ class Battle {
break;
} case 'fieldhtml': {
this.playbackState = Playback.Seeking; // force seeking to prevent controls etc
this.scene.setFrameHTML(Tools.sanitizeHTML(args[1]));
this.scene.setFrameHTML(BattleLog.sanitizeHTML(args[1]));
break;
} case 'controlshtml': {
this.scene.setControlsHTML(Tools.sanitizeHTML(args[1]));
this.scene.setControlsHTML(BattleLog.sanitizeHTML(args[1]));
break;
} default: {
this.scene.log('<div class="chat message-error">Unknown command: ' + Tools.escapeHTML(args[0]) + '</div>');
this.scene.log('<div class="chat message-error">Unknown command: ' + BattleLog.escapeHTML(args[0]) + '</div>');
if (this.errorCallback) this.errorCallback(this);
break;
}}
@ -4691,7 +4691,7 @@ class Battle {
if (!str) return;
if (str.charAt(0) !== '|' || str.substr(0, 2) === '||') {
if (str.charAt(0) === '|') str = str.substr(2);
this.scene.log('<div class="chat">' + Tools.escapeHTML(str) + '</div>', preempt);
this.scene.log('<div class="chat">' + BattleLog.escapeHTML(str) + '</div>', preempt);
return;
}
let args = ['done'];
@ -4751,9 +4751,9 @@ class Battle {
this.runMajor(args, kwargs, preempt);
}
} catch (e) {
this.scene.log('<div class="broadcast-red">Error parsing: ' + Tools.escapeHTML(str) + ' (' + Tools.escapeHTML('' + e) + ')</div>', preempt);
this.scene.log('<div class="broadcast-red">Error parsing: ' + BattleLog.escapeHTML(str) + ' (' + BattleLog.escapeHTML('' + e) + ')</div>', preempt);
if (e.stack) {
let stack = Tools.escapeHTML('' + e.stack).split('\n');
let stack = BattleLog.escapeHTML('' + e.stack).split('\n');
for (let i = 0; i < stack.length; i++) {
if (/\brun\b/.test(stack[i])) {
stack.length = i;

2
src/globals.d.ts vendored
View File

@ -29,7 +29,7 @@ declare var BattleStatuses: any;
declare var BattlePokemonSprites: any;
declare var BattlePokemonSpritesBW: any;
// defined in battle-dex-misc
// defined in battle-log-misc
declare function MD5(input: string): string;
declare function formatText(input: string): string;

View File

@ -8,6 +8,7 @@ window = global;
// Without making these modules, the best we can do is directly include them into this workspace.
eval('' + fs.readFileSync(`js/battle-scene-stub.js`));
eval('' + fs.readFileSync(`js/battle-log.js`));
eval('' + fs.readFileSync(`js/battle-dex.js`));
eval('' + fs.readFileSync(`js/battle-dex-data.js`));
eval('' + fs.readFileSync(`js/battle.js`));