Convert teambuilder from utilichart.js to search.js

The teambuilder is now running on search.js! Not all new features I'd
like to be in are in, but we've now reached feature parity, and most
of the bugs I found during testing have been fixed.

New in search.js is on-demand DOM loading, which basically means
much faster performance because instead of trying to load every single
row of e.g. the pokemon list at once, we just load the part that's
visible, and load the rest only when you scroll it into view.

Also new in search.js is a dexsearch-like feature, replacing the old
details-search system. The new filter system is simpler and more powerful
and has the same API as /dexsearch, although not all the more advanced
dexsearch features are supported.

On-demand DOM loading makes teambuilder loading pretty much completely
instantaneous. There are other small differences in how selection of
pokemon/items/abilities/moves works, but it should overall make more
sense.
This commit is contained in:
Guangcong Luo
2015-12-21 21:58:32 -06:00
parent 02f0c8e82c
commit 7fa72c1d20
9 changed files with 465 additions and 253 deletions

View File

@@ -19,10 +19,10 @@ module.exports = {
"BattleAbilities": false, "BattleAliases": false, "BattleBackdrops": 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,
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false,
"BattleSearchIndexOffset": false, "BattleSearchIndexType": false, "BattleStatIDs": false, "BattleStatNames": false, "BattleStats": false, "BattleStatusAnims": false, "BattleStatuses": false, "BattleTeambuilderTable": false,
// Generic global variables
"Config": false, "Chart": false, "soundManager": false, "Storage": false, "Tools": false,
"Config": false, "BattleSearch": false, "soundManager": false, "Storage": false, "Tools": false,
"app": false, "toId": false, "toRoomid": false, "toUserid": false, "toName": false, "hashColor": false, "MD5": false,
"ChatHistory": false, "Topbar": false, "UserList": false,

View File

@@ -103,3 +103,51 @@ const fs = require("fs");
}
console.log("DONE");
/*********************************************************
* Build teambuilder-tables.js
*********************************************************/
process.stdout.write("Building `data/teambuilder-tables.js`... ");
{
const BattleTeambuilderTable = {};
let buf = '// automatically built with githooks/build-indexes\n\n';
const pokemon = Object.keys(Tools.data.Pokedex);
pokemon.sort();
const tierTable = {};
for (const id of pokemon) {
const tier = Tools.getTemplate(id).tier;
if (!tierTable[tier]) tierTable[tier] = [];
tierTable[tier].push(id);
}
const tiers = BattleTeambuilderTable.tiers = [];
const formatSlices = BattleTeambuilderTable.formatSlices = {};
const tierOrder = ["Uber", "OU", "BL", "(OU)", "UU", "BL2", "RU", "BL3", "NU", "BL4", "PU", "NFE", "LC Uber", "LC"];
for (const tier of tierOrder) {
if (tier === "OU" || tier === "Uber" || tier === "UU" || tier === "RU" || tier === "NU" || tier === "PU" || tier === "LC") {
formatSlices[tier] = tiers.length;
}
if (!tierTable[tier]) continue;
if (tier.charAt(0) === '(') {
tiers.push(['header', tier.slice(1, -1) + " by technicality"]);
} else if (tier === "NFE") {
tiers.push(['header', "NFEs not in a higher tier"]);
} else {
tiers.push(['header', tier]);
}
tiers.push(...tierTable[tier]);
}
buf += 'exports.BattleTeambuilderTable = ' + JSON.stringify(BattleTeambuilderTable) + ';\n\n';
fs.writeFileSync('data/teambuilder-tables.js', buf);
}
console.log("DONE");

View File

@@ -164,7 +164,9 @@ ga('send', 'pageview');
<script src="//play.pokemonshowdown.com/data/items.js?"></script>
<script src="//play.pokemonshowdown.com/data/abilities.js?"></script>
<script src="//play.pokemonshowdown.com/js/utilichart.js?"></script>
<script src="//play.pokemonshowdown.com/data/search-index.js?"></script>
<script src="//play.pokemonshowdown.com/data/teambuilder-tables.js?"></script>
<script src="//play.pokemonshowdown.com/js/search.js?"></script>
<script src="//play.pokemonshowdown.com/data/aliases.js?" async="async"></script>

View File

@@ -409,16 +409,16 @@ var Tools = {
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 'data-pokemon':
if (!window.Chart) return '';
return '<div class="message"><ul class="utilichart">' + Chart.pokemonRow(Tools.getTemplate(target), '', {}, false, true) + '<li style=\"clear:both\"></li></ul></div>';
return '<div class="message"><ul class="utilichart">' + BattleSearch.renderPokemonRow(Tools.getTemplate(target), 0, 0) + '<li style=\"clear:both\"></li></ul></div>';
case 'data-item':
if (!window.Chart) return '';
return '<div class="message"><ul class="utilichart">' + Chart.itemRow(Tools.getItem(target), '', {}, false, true) + '<li style=\"clear:both\"></li></ul></div>';
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.Chart) return '';
return '<div class="message"><ul class="utilichart">' + Chart.abilityRow(Tools.getAbility(target), '', {}, false, true) + '<li style=\"clear:both\"></li></ul></div>';
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.Chart) return '';
return '<div class="message"><ul class="utilichart">' + Chart.moveRow(Tools.getMove(target), '', {}, false, true) + '<li style=\"clear:both\"></li></ul></div>';
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.escapeHTML(target) + '</div>';
case 'error':

View File

@@ -17,7 +17,6 @@
this.update();
},
focus: function () {
this.buildMovelists();
if (this.curTeam) {
this.curTeam.iconCache = '!';
this.curTeam.gen = this.getGen(this.curTeam.format);
@@ -49,7 +48,7 @@
'keydown .chartinput': 'chartKeydown',
'keyup .chartinput': 'chartKeyup',
'focus .chartinput': 'chartFocus',
'change .chartinput': 'chartChange',
'blur .chartinput': 'chartChange',
// drag/drop
'click .team': 'edit',
@@ -1384,6 +1383,15 @@
this.$el.html('<div class="teamwrapper">' + buf + '</div>');
if ($(window).width() < 640) this.show();
this.$chart = this.$('.teambuilder-results');
this.search = new BattleSearch(this.$chart, this.$chart);
var self = this;
// fun fact: Backbone DOM events don't support scroll...
// I guess scroll doesn't bubble like other events
this.$chart.on('scroll', function () {
if (self.curChartType in self.searchChartTypes) {
self.search.updateScroll();
}
});
},
updateSetTop: function () {
this.$('.teambar').html(this.renderTeambar());
@@ -1493,46 +1501,57 @@
},
curChartType: '',
curChartName: '',
updateChart: function () {
searchChartTypes: {
pokemon: 'pokemon',
ability: 'abilities',
move: 'moves',
item: 'items'
},
updateChart: function (pokemonChanged, wasIncomplete) {
var type = this.curChartType;
app.clearGlobalListeners();
if (type === 'stats') {
this.search.qType = null;
this.search.qName = null;
this.updateStatForm();
return;
}
if (type === 'details') {
this.search.qType = null;
this.search.qName = null;
this.updateDetailsForm();
return;
}
// cache movelist ref
var speciesid = toId(this.curSet.species);
var g6 = (this.curTeam.format && this.curTeam.format === 'vgc2016');
this.applyMovelist(g6, speciesid);
var $inputEl = this.$('input[name=' + this.curChartName + ']');
var q = $inputEl.val();
this.$chart.html('<em>Loading ' + this.curChartType + '...</em>');
var self = this;
if (this.updateChartTimeout) clearTimeout(this.updateChartTimeout);
this.updateChartTimeout = setTimeout(function () {
self.updateChartTimeout = null;
if (self.curChartType === 'stats' || self.curChartType === 'details' || !self.curChartName) return;
self.$chart.html(Chart.chart(self.$('input[name=' + self.curChartName + ']').val(), self.curChartType, true, _.bind(self.arrangeCallback[self.curChartType], self), null, self.curTeam.gen));
}, 10);
},
updateChartTimeout: null,
updateChartDelayed: function () {
// cache movelist ref
var speciesid = toId(this.curSet.species);
var g6 = (this.curTeam.format && this.curTeam.format === 'vgc2016');
this.applyMovelist(g6, speciesid);
var self = this;
if (this.updateChartTimeout) clearTimeout(this.updateChartTimeout);
this.updateChartTimeout = setTimeout(function () {
self.updateChartTimeout = null;
if (self.curChartType === 'stats' || self.curChartType === 'details') return;
self.$chart.html(Chart.chart(self.$('input[name=' + self.curChartName + ']').val(), self.curChartType, false, _.bind(self.arrangeCallback[self.curChartType], self), null, self.curTeam.gen));
}, 200);
if (pokemonChanged || this.search.qName !== this.curChartName) {
var cur = {};
if (type === 'move') {
cur[toId(this.$('input[name=move1]').val())] = 1;
cur[toId(this.$('input[name=move2]').val())] = 1;
cur[toId(this.$('input[name=move3]').val())] = 1;
cur[toId(this.$('input[name=move4]').val())] = 1;
} else {
cur[toId(q)] = 1;
}
if (type !== this.search.qType) {
this.$chart.scrollTop(0);
}
this.search.$inputEl = $inputEl;
this.search.setType(type, this.curTeam.format, this.curSet, cur);
this.qInitial = q;
this.search.qName = this.curChartName;
if (wasIncomplete) {
this.search.find(q);
if (this.search.q) this.$chart.find('a').first().addClass('hover');
}
} else if (q !== this.qInitial) {
this.qInitial = undefined;
this.search.find(q);
if (this.search.q) this.$chart.find('a').first().addClass('hover');
}
},
selectPokemon: function (i) {
i = +i;
@@ -1540,14 +1559,17 @@
if (set) {
this.curSet = set;
this.curSetLoc = i;
var name = this.curChartName || 'details';
if (name === 'details' || name === 'stats') {
if (!this.curChartName) {
this.curChartName = 'details';
this.curChartType = 'details';
}
if (this.curChartType in this.searchChartTypes) {
this.update();
this.updateChart();
this.updateChart(true);
this.$('input[name=' + this.curChartName + ']').select();
} else {
this.curChartName = '';
this.update();
this.$('input[name=' + name + ']').select();
this.updateChart(true);
}
}
},
@@ -1555,13 +1577,13 @@
if (!this.curSet) this.selectPokemon($(button).closest('li').val());
this.curChartName = 'stats';
this.curChartType = 'stats';
this.updateStatForm();
this.updateChart();
},
details: function (i, button) {
if (!this.curSet) this.selectPokemon($(button).closest('li').val());
this.curChartName = 'details';
this.curChartType = 'details';
this.updateDetailsForm();
this.updateChart();
},
/*********************************************************
@@ -2007,68 +2029,6 @@
* Set charts
*********************************************************/
arrangeCallback: {
pokemon: function (pokemon) {
if (!pokemon) {
if (this.curTeam) {
if (this.curTeam.format === 'ubers' || this.curTeam.format === 'anythinggoes') return ['Uber', 'OU', 'BL', 'OU only when combining mega and non-mega usage', 'UU', 'BL2', 'UU only when combining mega and non-mega usage', 'RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'ou') return ['OU', 'BL', 'OU only when combining mega and non-mega usage', 'UU', 'BL2', 'UU only when combining mega and non-mega usage', 'RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'cap') return ['CAP', 'OU', 'BL', 'OU only when combining mega and non-mega usage', 'UU', 'BL2', 'UU only when combining mega and non-mega usage', 'RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'uu') return ['UU', 'BL2', 'UU only when combining mega and non-mega usage', 'RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'ru') return ['RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'nu') return ['NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'pu') return ['PU', 'NFE', 'LC Uber', 'LC'];
if (this.curTeam.format === 'lc') return ['LC'];
}
return ['OU', 'Uber', 'BL', 'OU only when combining mega and non-mega usage', 'UU', 'BL2', 'UU only when combining mega and non-mega usage', 'RU', 'BL3', 'RU only when combining mega and non-mega usage', 'NU', 'BL4', 'NU only when combining mega and non-mega usage', 'PU', 'NFE', 'LC Uber', 'LC', 'Unreleased', 'CAP'];
}
var speciesid = toId(pokemon.species);
var tierData = exports.BattleFormatsData[speciesid];
if (!tierData) return 'Illegal';
var displayTier = {"(OU)": "OU only when combining mega and non-mega usage", "(UU)": "UU only when combining mega and non-mega usage", "(RU)": "RU only when combining mega and non-mega usage", "(NU)": "NU only when combining mega and non-mega usage"};
if (tierData.tier in displayTier) {
return displayTier[tierData.tier];
}
return tierData.tier;
},
item: function (item) {
if (!item) return ['Items'];
return 'Items';
},
ability: function (ability) {
if (!this.curSet) return;
var template = Tools.getTemplate(this.curSet.species);
var isMega = false;
if (template.forme.substr(0, 4) === 'Mega' && this.curTeam.format !== 'balancedhackmons') {
if (!ability) return ['Pre-Mega Abilities', 'Pre-Mega Hidden Ability'];
isMega = true;
template = Tools.getTemplate(template.baseSpecies);
}
if (!ability) return ['Abilities', 'Hidden Ability'];
if (!template.abilities) return 'Abilities';
if (ability.name === template.abilities['0']) return isMega ? 'Pre-Mega Abilities' : 'Abilities';
if (ability.name === template.abilities['1']) return isMega ? 'Pre-Mega Abilities' : 'Abilities';
if (ability.name === template.abilities['H']) return isMega ? 'Pre-Mega Hidden Ability' : 'Hidden Ability';
if (!this.curTeam || this.curTeam.format !== 'balancedhackmons') return 'Illegal';
},
move: function (move) {
if (!this.curSet) return;
if (!move) return ['Usable Moves', 'Moves', 'Usable Sketch Moves', 'Sketch Moves'];
var movelist = this.movelist;
if (!movelist) return 'Illegal';
if (!movelist[move.id]) {
if (movelist['sketch'] && move.id !== 'chatter' && move.id !== 'struggle') {
if (move.isViable) return 'Usable Sketch Moves';
return 'Sketch Moves';
}
if (!this.curTeam || this.curTeam.format !== 'balancedhackmons') return 'Illegal';
}
var speciesid = toId(this.curSet.species);
if (move.isViable) return 'Usable Moves';
return 'Moves';
}
},
chartTypes: {
pokemon: 'pokemon',
item: 'item',
@@ -2081,30 +2041,61 @@
details: 'details'
},
chartClick: function (e) {
this.chartSet($(e.currentTarget).data('name'), true);
if (this.search.addFilter(e.currentTarget)) {
this.$('input[name=' + this.curChartName + ']').val('').select();
this.search.find('');
return;
}
var val = $(e.currentTarget).data('entry').split(':')[1];
this.chartSet(val, true);
},
chartKeydown: function (e) {
var modifier = (e.shiftKey || e.ctrlKey || e.altKey || e.metaKey || e.cmdKey);
if (e.keyCode === 13 || (e.keyCode === 9 && !modifier)) {
if (!this.arrangeCallback[this.curChartType]) return;
if (!(this.curChartType in this.searchChartTypes)) return;
e.stopPropagation();
e.preventDefault();
var name = e.currentTarget.name;
this.$chart.html(Chart.chart(e.currentTarget.value, this.curChartType, false, _.bind(this.arrangeCallback[this.curChartType], this), null, this.curTeam.gen));
var val = Chart.firstResult;
this.search.find(e.currentTarget.value);
if (!this.search.q) return;
var $firstResult = this.$chart.find('a').first();
if (this.search.addFilter($firstResult[0])) {
$(e.currentTarget).val('').select();
this.search.find('');
return;
}
var val = $firstResult.data('entry').split(':')[1];
this.chartSet(val, true);
return;
} else if (e.keyCode === 27 || e.keyCode === 8) { // esc, backspace
if (!e.currentTarget.value && this.search.removeFilter()) {
this.search.find('');
return;
}
} else if (e.keyCode === 188) {
var $firstResult = this.$chart.find('a').first();
if (!this.search.q) return;
if (this.search.addFilter($firstResult[0])) {
e.preventDefault();
e.stopPropagation();
$(e.currentTarget).val('').select();
this.search.find('');
return;
}
}
},
chartKeyup: function () {
this.updateChartDelayed();
this.updateChart();
},
chartFocus: function (e) {
var $target = $(e.currentTarget);
var name = e.currentTarget.name;
var type = this.chartTypes[name];
$target.removeClass('incomplete');
var wasIncomplete = false;
if ($target.hasClass('incomplete')) {
wasIncomplete = true;
$target.removeClass('incomplete');
}
if (this.curChartName === name) return;
@@ -2123,21 +2114,32 @@
this.curChartName = name;
this.curChartType = type;
this.updateChart();
this.updateChart(false, wasIncomplete);
},
chartChange: function (e) {
var name = e.currentTarget.name;
var type = this.chartTypes[name];
var arrange = null;
if (this.arrangeCallback[this.curChartType]) {
arrange = _.bind(this.arrangeCallback[this.curChartType], this);
}
this.$chart.html(Chart.chart(e.currentTarget.value, type, false, arrange, null, this.curTeam.gen));
var val = Chart.firstResult;
if (this.curChartName !== name) return;
var id = toId(e.currentTarget.value);
if (toId(val) !== id) {
$(e.currentTarget).addClass('incomplete');
return;
var val = '';
switch (name) {
case 'pokemon':
val = (id in BattlePokedex ? BattlePokedex[id].name : '');
break;
case 'ability':
val = (id in BattleAbilities ? BattleAbilities[id].name : '');
break;
case 'item':
val = (id in BattleItems ? BattleItems[id].name : '');
break;
case 'move1': case 'move2': case 'move3': case 'move4':
val = (id in BattleMovedex ? BattleMovedex[id].name : '');
break;
}
if (!val) {
if (name === 'pokemon' || name === 'ability' || id) {
$(e.currentTarget).addClass('incomplete');
return;
}
}
this.chartSet(val);
},
@@ -2167,7 +2169,7 @@
if (!this.curSet.moves[0]) this.curSet.moves[0] = '';
this.curSet.moves[1] = val;
this.chooseMove(val);
this.$('input[name=move3]').select();
if (selectNext) this.$('input[name=move3]').select();
break;
case 'move3':
if (!this.curSet.moves[0]) this.curSet.moves[0] = '';
@@ -2216,7 +2218,10 @@
var set = this.curSet;
var template = Tools.getTemplate(val);
var newPokemon = !set.species;
if (!template.exists || set.species === template.species) return;
if (!template.exists || set.species === template.species) {
if (selectNext) this.$('input[name=item]').select();
return;
}
set.name = template.species;
set.species = val;
@@ -2728,46 +2733,6 @@
// initialization
buildMovelists: function () {
if (Tools.movelists) return;
if (!window.BattlePokedex) return;
Tools.movelists = {};
Tools.g6movelists = {};
for (var pokemon in window.BattlePokedex) {
var template = Tools.getTemplate(pokemon);
var moves = {};
var g6moves = {};
var alreadyChecked = {};
do {
alreadyChecked[template.speciesid] = true;
if (template.learnset) {
for (var l in template.learnset) {
moves[l] = true;
if (template.learnset[l].length) g6moves[l] = true;
}
}
if (template.speciesid === 'shaymin') {
template = Tools.getTemplate('shayminsky');
} else if (toId(template.baseSpecies) !== toId(template.species) && toId(template.baseSpecies) !== 'pikachu' && toId(template.baseSpecies) !== 'wormadam' && toId(template.baseSpecies) !== 'kyurem') {
template = Tools.getTemplate(template.baseSpecies);
} else {
template = Tools.getTemplate(template.prevo);
}
} while (template && template.species && !alreadyChecked[template.speciesid]);
Tools.movelists[pokemon] = moves;
Tools.g6movelists[pokemon] = g6moves;
}
},
applyMovelist: function (g6only, speciesid) {
this.buildMovelists();
if (!Tools.movelists) {
this.movelist = false;
} else if (g6only) {
this.movelist = Tools.g6movelists[speciesid];
} else {
this.movelist = Tools.movelists[speciesid];
}
},
getGen: function (format) {
format = '' + format;
if (format.substr(0, 3) !== 'gen') return 6;

View File

@@ -21,12 +21,16 @@
this.q = undefined; // uninitialized
this.qType = '';
this.defaultResultSet = null;
this.legalityFilter = null;
this.legalityLabel = "Illegal";
this.exactMatch = false;
this.resultSet = null;
this.filters = null;
this.renderedIndex = 0;
this.renderingDone = true;
this.cur = {};
this.$inputEl = null;
var self = this;
this.$el.on('mouseover', '.more-autoscroll', function () {
@@ -40,6 +44,21 @@
self.updateScroll(true);
}
});
this.$el.on('click', '.filter', function (e) {
e.preventDefault();
if (!self.filters) return;
// delete filter
for (var i = 0; i < self.filters.length; i++) {
if (e.currentTarget.value === self.filters[i].join(':')) {
self.filters.splice(i, 1);
if (!self.filters.length) self.filters = null;
self.q = undefined;
self.find('');
break;
}
}
if (self.$inputEl) self.$inputEl.focus();
});
}
Search.prototype.$ = function (query) {
@@ -78,10 +97,16 @@
this.resultSet = null;
var qType = this.qType;
if (!query) {
if (!this.filters && this.defaultResultSet) {
this.renderedIndex = 0;
this.renderingDone = false;
this.updateScroll();
return true;
}
if (qType === 'pokemon') {
this.allPokemon();
return true;
} else if (qType === 'moves') {
} else if (qType === 'move') {
this.allMoves();
return true;
}
@@ -93,9 +118,9 @@
var qTypeIndex = -1;
switch (qType) {
case 'pokemon': qTypeIndex = 0; break;
case 'moves': qTypeIndex = 2; break;
case 'items': qTypeIndex = 3; break;
case 'abilities': qTypeIndex = 4; break;
case 'move': qTypeIndex = 2; break;
case 'item': qTypeIndex = 3; break;
case 'ability': qTypeIndex = 4; break;
}
var qFilterType = '';
@@ -107,6 +132,10 @@
}
var i = Search.getClosest(query);
if (query in BattleAliases) {
query = toId(BattleAliases[query]);
i = Search.getClosest(query);
}
if (!BattleSearchIndex[i]) i--;
if (BattleSearchIndex[i - 1] && BattleSearchIndex[i - 1] === query) i--;
this.exactMatch = (query === BattleSearchIndex[i]);
@@ -117,34 +146,37 @@
var nearMatch = (BattleSearchIndex[i].substr(0, query.length) !== query);
this.renderingDone = false;
if (nearMatch && i) i--;
for (var j = 0; j < 15; j++) {
var id = BattleSearchIndex[i + j];
var type = BattleSearchIndexType[i + j];
for (var count = 0; count < 15; i++) {
var id = BattleSearchIndex[i];
var type = BattleSearchIndexType[i];
var matchLength = query.length;
if (!id) break;
if (id.substr(0, query.length) !== query) {
this.renderingDone = true;
if (!(nearMatch && j <= 1)) break;
if (!(nearMatch && count <= 1)) break;
matchLength = 0;
} else {
matchLength += (BattleSearchIndexOffset[i + j][matchLength - 1] || '0').charCodeAt(0) - 48;
matchLength += (BattleSearchIndexOffset[i][matchLength - 1] || '0').charCodeAt(0) - 48;
}
var typeIndex = typeTable[type];
if (qType === 'pokemon' && (typeIndex === 3 || typeIndex > 5)) continue;
if (qType === 'moves' && (typeIndex !== 6 && typeIndex > 2)) continue;
if (qType === 'move' && (typeIndex !== 6 && typeIndex > 2)) continue;
if ((qType === 'ability' || qType === 'item') && typeIndex !== qTypeIndex) continue;
if (qFilterType === 'type' && typeIndex !== 1) continue;
var errorMessage = '';
if (qType && qTypeIndex !== typeIndex) errorMessage = '<span class="col filtercol"><em>Filter</em></span>';
else if (this.legalityFilter && !(id in this.legalityFilter)) errorMessage = '<span class="col illegalcol"><em>' + this.legalityLabel + '</em></span>';
if (j === 0 && this.exactMatch) {
if (count === 0 && this.exactMatch) {
topbufIndex = typeIndex;
}
if (!bufs[typeIndex]) bufs[typeIndex] = '<li class="resultheader"><h3>' + typeName[type] + '</h3></li>';
bufs[typeIndex] += Search.renderRow(id, type, 0, matchLength, errorMessage);
count++;
}
var topbuf = '';
@@ -162,32 +194,29 @@
if (nearMatch && !qType) topbuf = '<li class="notfound"><em>No exact match found. The closest matches alphabetically are:</em></li>' + topbuf;
this.el.innerHTML = '<ul class="utilichart">' + topbuf + bufs.join('') + '</ul>' + (this.renderingDone ? '' : '<ul class="utilichart"><li class="more"><button class="utilichart-all">All results</button></li></ul>');
this.el.innerHTML = '<ul class="utilichart">' + topbuf + bufs.join('') + '</ul>' + (this.renderingDone || qType ? '' : '<ul class="utilichart"><li class="more"><button class="utilichart-all">All results</button></li></ul>');
return true;
};
Search.prototype.addFilter = function (name, result) {
Search.prototype.addFilter = function (node) {
if (!node.dataset.entry) return;
var entry = node.dataset.entry.split(':');
if (this.qType === 'pokemon') {
result = result.split('/');
if (result[0] !== 'types' && result[0] !== 'moves' && result[0] !== 'abilities' && result[0] !== 'egggroups') return;
if (result[0] === 'types') result[1] = name;
if (result[0] === 'abilities') result[1] = name;
if (result[0] === 'egggroups') result[1] = name;
if (entry[0] !== 'type' && entry[0] !== 'move' && entry[0] !== 'ability' && entry[0] !== 'egggroup') return;
if (entry[0] === 'move') entry[1] = toId(entry[1]);
if (!this.filters) this.filters = [];
this.q = undefined;
for (var i = 0; i < this.filters.length; i++) {
if (this.filters[i][0] === result[0] && this.filters[i][1] === result[1]) {
if (this.filters[i][0] === entry[0] && this.filters[i][1] === entry[1]) {
return true;
}
}
this.filters.push(result);
this.filters.push(entry);
return true;
} else if (this.qType === 'moves') {
result = result.split('/');
if (result[0] !== 'types' && result[0] !== 'categories' && result[0] !== 'pokemon') return;
if (result[0] === 'types') result[1] = name;
if (result[0] === 'categories') result[1] = name;
} else if (this.qType === 'move') {
if (entry[0] !== 'type' && entry[0] !== 'category' && entry[0] !== 'pokemon') return;
if (entry[0] === 'pokemon') entry[1] = toId(entry[1]);
if (!this.filters) this.filters = [];
this.filters.push(result);
this.filters.push(entry);
this.q = undefined;
return true;
}
@@ -300,34 +329,35 @@
this.updateScroll();
};
Search.prototype.getFilterText = function (q) {
var buf = 'Filters: ';
var buf = '<p>Filters: ';
for (var i = 0; i < this.filters.length; i++) {
if (i) buf += ', ';
var text = this.filters[i][1];
if (this.filters[i][0] === 'moves') text = Tools.getMove(text).name;
if (this.filters[i][0] === 'move') text = Tools.getMove(text).name;
if (this.filters[i][0] === 'pokemon') text = Tools.getTemplate(text).name;
buf += '<strong style="padding:1px 3px; border-radius: 3px; border: 1px solid #777">' + text + '</strong>';
buf += '<button class="filter" value="' + Tools.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;
if (!q) buf += '<small style="color: #888">(backspace = delete filter)</small>';
return buf + '</p>';
};
Search.prototype.filteredPokemon = function () {
var resultSet = [['html', this.getFilterText(), 0]];
resultSet.push(['header', "Filtered results", 0]);
var illegalResultSet = [];
var filters = this.filters;
for (var id in BattlePokedex) {
var template = BattlePokedex[id];
if (template.exists === false) continue;
for (var i = 0; i < filters.length; i++) {
if (filters[i][0] === 'types') {
if (filters[i][0] === 'type') {
var type = filters[i][1];
if (template.types[0] !== type && template.types[1] !== type) break;
} else if (filters[i][0] === 'egggroups') {
} else if (filters[i][0] === 'egggroup') {
var egggroup = filters[i][1];
if (template.eggGroups[0] !== egggroup && template.eggGroups[1] !== egggroup) break;
} else if (filters[i][0] === 'abilities') {
} else if (filters[i][0] === 'ability') {
var ability = filters[i][1];
if (template.abilities['0'] !== ability && template.abilities['1'] !== ability && template.abilities['H'] !== ability) break;
} else if (filters[i][0] === 'moves') {
} else if (filters[i][0] === 'move') {
var learned = false;
var learnsetid = id;
while (true) {
@@ -345,9 +375,13 @@
}
}
if (i < filters.length) continue;
resultSet.push(['pokemon', id, 0]);
if (this.legalityFilter && !(id in this.legalityFilter)) {
illegalResultSet.push(['pokemon', id, -1]);
} else {
resultSet.push(['pokemon', id, 0]);
}
}
this.resultSet = resultSet;
this.resultSet = resultSet.concat(illegalResultSet);
this.renderedIndex = 0;
this.renderingDone = false;
this.updateScroll();
@@ -355,13 +389,15 @@
Search.prototype.filteredMoves = function () {
var resultSet = [['html', this.getFilterText(), 0]];
resultSet.push(['header', "Filtered results", 0]);
var illegalResultSet = [];
var filters = this.filters;
for (var id in BattleMovedex) {
var move = BattleMovedex[id];
if (move.exists === false) continue;
for (var i = 0; i < filters.length; i++) {
if (filters[i][0] === 'types') {
if (filters[i][0] === 'type') {
if (move.type !== filters[i][1]) break;
} else if (filters[i][0] === 'categories') {
} else if (filters[i][0] === 'category') {
if (move.category !== filters[i][1]) break;
} else if (filters[i][0] === 'pokemon') {
var learned = false;
@@ -381,9 +417,13 @@
}
}
if (i < filters.length) continue;
resultSet.push(['move', id, 0]);
if (this.legalityFilter && !(id in this.legalityFilter)) {
illegalResultSet.push(['move', id, -1]);
} else {
resultSet.push(['move', id, 0]);
}
}
this.resultSet = resultSet;
this.resultSet = resultSet.concat(illegalResultSet);
this.renderedIndex = 0;
this.renderingDone = false;
this.updateScroll();
@@ -397,29 +437,142 @@
if (forceAdd && finalIndex < i + 20) finalIndex = i + 20;
if (finalIndex <= i) return;
var resultSet = this.resultSet || this.defaultResultSet;
var buf = '';
while (i < finalIndex) {
if (!this.resultSet[i]) {
if (!resultSet[i]) {
this.renderingDone = true;
break;
}
var row = this.resultSet[i];
var row = resultSet[i];
buf += Search.renderRow(row[1], row[0], 0, row[2]);
var errorMessage = '';
if (row[2] < 0) {
errorMessage = '<span class="col illegalcol"><em>' + this.legalityLabel + '</em></span>';
row[2] = 0;
}
buf += Search.renderRow(row[1], row[0], 0, row[2], errorMessage, row[1] in this.cur ? ' class="cur"' : '');
i++;
}
if (!this.renderedIndex) {
this.el.innerHTML = '<ul class="utilichart" style="height:' + (this.resultSet.length * 33) + 'px">' + buf + '</ul>';
// (this.renderingDone ? '' : '<ul class="utilichart"><li class="more more-autoscroll"><button>More</button></li></ul>');
this.el.innerHTML = '<ul class="utilichart" style="height:' + (resultSet.length * 33) + 'px">' + buf + '</ul>';
} else {
$(this.el.firstChild).append(buf);
// if (this.renderingDone) {
// this.$el.find('li.more').parent().remove();
// }
}
this.renderedIndex = i;
};
Search.prototype.setType = function (qType, format, set, cur) {
if (!format) format = '';
if (this.qType !== qType) this.filters = null;
this.qType = qType;
this.q = undefined;
this.cur = cur || {};
this.legalityFilter = {};
this.legalityLabel = "Illegal";
var gen = 6;
if (format.slice(0, 3) === 'gen') gen = (Number(format.charAt(3)) || 6);
var requirePentagon = (format === 'vgc2016');
var template;
this.resultSet = null;
this.defaultResultSet = null;
switch (qType) {
case 'pokemon':
if (!BattleTeambuilderTable.tierSet) {
BattleTeambuilderTable.tierSet = BattleTeambuilderTable.tiers.map(function (r) {
if (typeof r === 'string') return ['pokemon', r, 0];
return [r[0], r[1], 0];
});
BattleTeambuilderTable.tiers = null;
}
var tierSet = BattleTeambuilderTable.tierSet;
if (format === 'ou') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.OU);
if (format === 'uu') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.UU);
if (format === 'ru') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.RU);
if (format === 'nu') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.NU);
if (format === 'lc') tierSet = tierSet.slice(BattleTeambuilderTable.formatSlices.LC);
this.defaultResultSet = tierSet;
this.legalityLabel = "Banned";
break;
case 'item':
var itemSet = [['header', "Items", 0]];
for (var id in BattleItems) {
itemSet.push(['item', id, 0]);
}
this.defaultResultSet = itemSet;
this.legalityFilter = null;
break;
case 'ability':
template = Tools.getTemplate(set.species);
var abilitySet = [['header', "Abilities", 0]];
if (template.isMega) {
abilitySet.unshift(['html', '<p>Will be <strong>' + Tools.escapeHTML(template.abilities['0']) + '</strong> after Mega Evolving.</p>']);
template = Tools.getTemplate(template.baseSpecies);
}
abilitySet.push(['ability', toId(template.abilities['0']), 0]);
if (template.abilities['1']) {
abilitySet.push(['ability', toId(template.abilities['1']), 0]);
}
if (template.abilities['H']) {
abilitySet.push(['header', "Hidden Ability", 0]);
abilitySet.push(['ability', toId(template.abilities['H']), 0]);
}
this.defaultResultSet = abilitySet;
break;
case 'move':
template = Tools.getTemplate(set.species);
var moves = [];
while (true) {
var learnsetTemplate = BattleLearnsets[toId(template.baseSpecies)];
if (learnsetTemplate) {
for (var l in learnsetTemplate.learnset) {
if (requirePentagon && !learnsetTemplate.learnset[l].length) continue;
if (moves.indexOf(l) >= 0) continue;
moves.push(l);
if (l === 'hiddenpower') {
moves.push('hiddenpowerbug', 'hiddenpowerdark', 'hiddenpowerdragon', 'hiddenpowerelectric', 'hiddenpowerfighting', 'hiddenpowerfire', 'hiddenpowerflying', 'hiddenpowerghost', 'hiddenpowergrass', 'hiddenpowerground', 'hiddenpowerice', 'hiddenpowerpoison', 'hiddenpowerpsychic', 'hiddenpowerrock', 'hiddenpowersteel', 'hiddenpowerwater');
}
}
}
if (template.baseSpecies && !learnsetTemplate) template = BattlePokedex[toId(template.baseSpecies)];
else if (template.prevo) template = BattlePokedex[template.prevo];
else break;
}
moves.sort();
var usableMoves = [];
var uselessMoves = [];
for (var i = 0; i < moves.length; i++) {
var id = moves[i];
if (BattleMovedex[id] && BattleMovedex[id].isViable) {
if (!usableMoves.length) usableMoves.push(['header', "Moves", 0]);
usableMoves.push(['move', id, 0]);
} else {
if (!uselessMoves.length) uselessMoves.push(['header', "Usually useless moves", 0]);
uselessMoves.push(['move', id, 0]);
}
}
this.defaultResultSet = usableMoves.concat(uselessMoves);
break;
}
if (this.legalityFilter) {
for (var i = 0; i < this.defaultResultSet.length; i++) {
if (this.defaultResultSet[i][0] !== 'header') {
this.legalityFilter[this.defaultResultSet[i][1]] = 1;
}
}
}
this.renderedIndex = 0;
this.renderingDone = false;
this.find('');
};
Search.getClosest = function (query) {
// binary search through the index!
@@ -439,13 +592,13 @@
return left;
};
//
// Rendering functions
//
// These are all static!
//
/*********************************************************
* Rendering functions
*********************************************************/
Search.renderRow = function (id, type, matchStart, matchLength, errorMessage) {
// These are all static!
Search.renderRow = function (id, type, matchStart, matchLength, errorMessage, attrs) {
// errorMessage = '<span class="col illegalcol"><em>' + errorMessage + '</em></span>';
switch (type) {
case 'html':
@@ -454,16 +607,16 @@
return '<li class="result"><h3>' + id + '</h3></li>';
case 'pokemon':
var pokemon = BattlePokedex[id];
return Search.renderPokemonRow(pokemon, matchStart, matchLength, errorMessage);
return Search.renderPokemonRow(pokemon, matchStart, matchLength, errorMessage, attrs);
case 'move':
var move = BattleMovedex[id];
return Search.renderMoveRow(move, matchStart, matchLength, errorMessage);
return Search.renderMoveRow(move, matchStart, matchLength, errorMessage, attrs);
case 'item':
var item = BattleItems[id];
return Search.renderItemRow(item, matchStart, matchLength, errorMessage);
return Search.renderItemRow(item, matchStart, matchLength, errorMessage, attrs);
case 'ability':
var ability = BattleAbilities[id];
return Search.renderAbilityRow(ability, matchStart, matchLength, errorMessage);
return Search.renderAbilityRow(ability, matchStart, matchLength, errorMessage, attrs);
case 'type':
var type = {name: id[0].toUpperCase() + id.substr(1)};
return Search.renderTypeRow(type, matchStart, matchLength, errorMessage);
@@ -487,10 +640,11 @@
}
return 'Error: not found';
};
Search.renderPokemonRow = function (pokemon, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'pokemon/' + toId(pokemon.species) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-name="' + Tools.escapeHTML(pokemon.species) + '">';
Search.renderPokemonRow = function (pokemon, matchStart, matchLength, errorMessage, attrs) {
if (!attrs) attrs = '';
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) + '">';
// number
buf += '<span class="col numcol">' + (pokemon.num >= 0 ? pokemon.num : 'CAP') + '</span> ';
@@ -545,7 +699,7 @@
if (!ability) continue;
if (i === '1') buf += '<br />';
if (i === 'H') ability = '</span><span class="col abilitycol"><em>' + pokemon.abilities[i] + '</em>';
if (i === 'H') ability = '</span><span class="col abilitycol' + (pokemon.unreleasedHidden ? ' unreleasedhacol' : ' hacol') + '"><em>' + pokemon.abilities[i] + '</em>';
buf += ability;
}
if (!pokemon.abilities['H']) buf += '</span><span class="col abilitycol">';
@@ -572,7 +726,7 @@
Search.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-name="' + Tools.escapeHTML(pokemon.species) + '">';
var buf = '<a' + attrs + ' data-entry="pokemon:' + Tools.escapeHTML(pokemon.species) + '">';
// tag
buf += '<span class="col tagcol shorttagcol">' + tag + '</span> ';
@@ -638,10 +792,11 @@
return buf;
};
Search.renderItemRow = function (item, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'items/' + toId(item.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-name="' + Tools.escapeHTML(item.name) + '">';
Search.renderItemRow = function (item, matchStart, matchLength, errorMessage, attrs) {
if (!attrs) attrs = '';
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) + '">';
// icon
buf += '<span class="col itemiconcol">';
@@ -668,10 +823,11 @@
return buf;
};
Search.renderAbilityRow = function (ability, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'abilities/' + toId(ability.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-name="' + Tools.escapeHTML(ability.name) + '">';
Search.renderAbilityRow = function (ability, matchStart, matchLength, errorMessage, attrs) {
if (!attrs) attrs = '';
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) + '">';
// name
var name = ability.name;
@@ -692,10 +848,11 @@
return buf;
};
Search.renderMoveRow = function (move, matchStart, matchLength, 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-name="' + Tools.escapeHTML(move.name) + '">';
Search.renderMoveRow = function (move, matchStart, matchLength, errorMessage, attrs) {
if (!attrs) attrs = '';
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) + '">';
// name
var name = move.name;
@@ -744,7 +901,7 @@
Search.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-name="' + Tools.escapeHTML(move.name) + '">';
var buf = '<a' + attrs + ' data-entry="move:' + Tools.escapeHTML(move.name) + '">';
// name
var name = move.name;
@@ -779,7 +936,7 @@
Search.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-name="' + Tools.escapeHTML(move.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="move:' + Tools.escapeHTML(move.name) + '">';
// tag
buf += '<span class="col tagcol">' + tag + '</span> ';
@@ -817,7 +974,7 @@
Search.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-name="' + Tools.escapeHTML(type.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="type:' + Tools.escapeHTML(type.name) + '">';
// name
var name = type.name;
@@ -844,7 +1001,7 @@
Search.renderCategoryRow = function (category, matchStart, matchLength, errorMessage) {
var attrs = '';
if (Search.urlRoot) attrs = ' href="' + Search.urlRoot + 'categories/' + toId(category.name) + '" data-target="push"';
var buf = '<li class="result"><a' + attrs + ' data-name="' + Tools.escapeHTML(category.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="category:' + Tools.escapeHTML(category.name) + '">';
// name
var name = category.name;
@@ -871,7 +1028,7 @@
Search.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-name="' + Tools.escapeHTML(egggroup.name) + '">';
var buf = '<li class="result"><a' + attrs + ' data-entry="egggroup:' + Tools.escapeHTML(egggroup.name) + '">';
// name
var name = egggroup.name;

View File

@@ -2726,12 +2726,13 @@ a.ilink.yours {
border-radius: 4px;
cursor: pointer;
}
.teambuilder-results .result a:hover {
.teambuilder-results .result a:hover,
.teambuilder-results .result a.hover {
border-color: #D8D8D8;
background: #F8F8F8;
}
.teambuilder-results .firstresult a,
.teambuilder-results .firstresult a:hover {
.teambuilder-results .result a.cur:hover,
.teambuilder-results .result a.cur.hover {
border-color: #CCCCCC;
background: #F2F2F2;
}

View File

@@ -15,12 +15,42 @@
margin: 0 5px 1px 5px;
text-decoration: none;
}
.utilichart li > a.cur {
border-color: #CCCCCC;
background: #F2F2F2;
}
.utilichart .filter {
padding:1px 3px;
border-radius: 3px;
border: 1px solid #777;
background: #EEE;
font-size: 9pt;
font-family: Verdana, sans-serif;
font-weight: bold;
cursor: pointer;
}
.utilichart .filter:hover {
color: #777777;
text-decoration: line-through;
}
.utilichart .filter i {
color: #999999;
}
.utilichart .filter:hover i {
color: #BB2222;
}
.resultheader,
.utilichart li.resultheader,
.utilichart .result {
height: 32px;
padding: 1px 0 0 0;
}
.result p,
.resultheader p,
.utilichart .result p {
padding: 7px 0 0 8px;
margin: 0;
}
.utilichart h3,
.dexentry h3,
.resultheader h3 {
@@ -185,6 +215,13 @@
width: 90px;
text-align: center;
}
.utilichart .hiddenabilitycol {
font-style: italic;
}
.utilichart .unreleasedhiddenabilitycol {
font-style: italic;
text-decoration: line-through;
}
.utilichart b {
color: #4488CC;
text-decoration: underline;

View File

@@ -116,7 +116,9 @@
<script src="data/items.js"></script>
<script src="data/abilities.js"></script>
<script src="js/utilichart.js"></script>
<script src="data/search-index.js"></script>
<script src="data/teambuilder-tables.js"></script>
<script src="js/search.js"></script>
<script src="data/aliases.js" async="async"></script>