Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 52 additions & 25 deletions badges.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@property {boolean} [unlocked]
@property {boolean} hidden
@property {boolean} [secret]
@property {boolean} [secretCondition]
@property {number} originalOrder
*/

Expand Down Expand Up @@ -202,7 +203,7 @@ function initBadgeControls() {
return -1;
return 1;
}
return a.originalOrder - b.originalOrder;
return sortOrderDesc ? b.originalOrder - a.originalOrder : a.originalOrder - b.originalOrder;
};

// If we already have cache and it hasn't been invalidated, only update the modal.
Expand Down Expand Up @@ -566,6 +567,14 @@ function initBadgeControls() {
exactMatch = true;
}

function isMatch(haystack, needle, exact) {
if (!haystack) return false;
if (exact) {
return haystack.localeCompare(needle, undefined, { sensitivity: 'base', usage: 'search' }) === 0;
}
return haystack.toLocaleLowerCase().indexOf(needle) > -1;
}

const gameVisibilities = {};
const gameGroupVisibilities = {};

Expand All @@ -579,9 +588,10 @@ function initBadgeControls() {
visible &= item.el.classList.contains('locked') === !parseInt(unlockStatus);
if (searchTerm.trim().length) {
switch (searchMode) {
case 'name':
case 'name': {
visible &= item.title && (exactMatch ? item.title === parsedSearchTerm : item.title.indexOf(parsedSearchTerm) > -1);
break;
}
case 'location': {
const localizedLocation = gameLocalizedMapLocations[item.game]?.[item.mapId];
let title = determineTitle(localizedLocation, item.mapX, item.mapY);
Expand All @@ -596,13 +606,22 @@ function initBadgeControls() {
title = globalConfig.lang === 'ja' ? desc.titleJP : desc.title;
}
}
visible &= title && (exactMatch ? title.toLocaleLowerCase() === parsedSearchTerm : title.toLocaleLowerCase().indexOf(parsedSearchTerm) > -1);
visible &= isMatch(title, parsedSearchTerm, exactMatch);
break;
}
case 'artist': {
visible &= item.art && (exactMatch ? item.art.split(/ & |, /).includes(parsedSearchTerm) : item.art.indexOf(parsedSearchTerm) > -1);
break;
}
case 'contents': {
const gameBadgeLocalizations = localizedBadges && localizedBadges[item.game];
const badgeLocalization = gameBadgeLocalizations && gameBadgeLocalizations[item.badgeId];
visible &= badgeLocalization && (
(item.unlocked || !item.secret) && isMatch(badgeLocalization.description, parsedSearchTerm, exactMatch)
|| (item.unlocked || !item.secretCondition) && isMatch(badgeLocalization.condition, parsedSearchTerm, exactMatch)
);
break;
}
}
}
if (!(item.game in gameVisibilities)) {
Expand Down Expand Up @@ -638,29 +657,30 @@ function initBadgeControls() {
return;
}

let parsedValue = this.value;
let modifier = null;
if (parsedValue.startsWith('=')) {
parsedValue = parsedValue.slice(1);
modifier = 'exactMatch';
}
let value = parsedValue;
if (modifier)
value += localizedMessages.badges.search.modifier.template.replace('{MODIFIER}', localizedMessages.badges.search.modifier[modifier]);

// mappings for user input sanitization
const map = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
"/": '&#x2F;',
};
const reg = /[&<>"'/]/ig;
value = value.replace(reg, match => map[match]);

for (const span of badgeDropdown.querySelectorAll('.dropdownItem span')) {
let parsedValue = this.value;
let modifier = null;
if (parsedValue.startsWith('=')) {
parsedValue = parsedValue.slice(1);
modifier = 'exactMatch';
}
let value = parsedValue;
if (modifier)
value += localizedMessages.badges.search.modifier.template.replace('{MODIFIER}', localizedMessages.badges.search.modifier[modifier]);

// mappings for user input sanitization
const map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
"/": '&#x2F;',
};

const reg = /[&<>"'/]/ig;
span.innerHTML = value.replace(reg, (match)=>(map[match]));
span.innerHTML = value;
}
};

Expand Down Expand Up @@ -703,6 +723,9 @@ function initBadgeControls() {
const searchArtist = document.getElementById('searchArtist').parentElement;
onDropdownActivate(searchArtist, () => searchBadges('artist'));

const searchContents = document.getElementById('searchContents').parentElement;
onDropdownActivate(searchContents, () => searchBadges('contents'));

function searchBadges(mode) {
badgeDropdown.classList.add('hidden');
updateBadgeVisibility(mode);
Expand Down Expand Up @@ -1083,6 +1106,10 @@ function getBadgeItem(badge, includeTooltip, emptyIcon, lockedIcon, scaled, filt
percent: badge.percent,
badgeId: badge.badgeId,
art: '',
originalOrder: badge.originalOrder,
unlocked: badge.unlocked,
secret: badge.secret,
secretCondition: badge.secretCondition,
};
item.dataset.cacheIndex = badgeFilterCache.push(filterItem) - 1;
}
Expand Down
8 changes: 8 additions & 0 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -1080,6 +1080,7 @@ class="joystickBase" />
<label for="badgeSortOrder" class="unselectable" data-i18n="[html]modal.badges.fields.sortOrder.label">Sort Order:&nbsp;</label>
<select id="badgeSortOrder">
<option value="" data-i18n="[html]modal.badges.fields.sortOrder.values.default">Default</option>
<option value="_desc" data-i18n="[html]modal.badges.fields.sortOrder.values.reverse">Reversed</option>
</select>
</div>
<div class="uiControl">
Expand All @@ -1101,6 +1102,10 @@ class="joystickBase" />
<path
d="m10 4a1 1 90 000 5 1 1 90 000-5M5 17c0-5 1-7 5-7s5 2 5 7q-5 2-10 0" />
</svg>
<svg data-kind="contents" class="icon searchIcon hidden" viewBox="0 0 10 10" fill="none"
xmlns="http://www.w3.org/2000/svg" height="12">
<path d="M7 6H16M2 10H16M2 14H16" />
</svg>
<input id="badgeSearch" type="text" autocomplete="off">
<div class="dropdown hidden" id="badgeDropdown">
<div class="dropdownItem" tabindex="0">
Expand All @@ -1112,6 +1117,9 @@ class="joystickBase" />
<div class="dropdownItem" tabindex="0">
<i data-i18n="[html]modal.badges.fields.search.artist">Artist:</i> <span id="searchArtist"></span>
</div>
<div class="dropdownItem" tabindex="0">
<i data-i18n="[html]modal.badges.fields.search.contents">Text:</i> <span id="searchContents"></span>
</div>
</div>
</div>
<a id="badgeSearchClearLink" href="javascript:void(0);" class="unselectable hidden">✖</a>
Expand Down
3 changes: 2 additions & 1 deletion lang/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "البحث:&nbsp;",
"name": "Name:",
"location": "Location:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Suchen nach:&nbsp;",
"name": "Name:",
"location": "Ort:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Search:&nbsp;",
"name": "Name:",
"location": "Location:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/eo.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Serĉi:&nbsp;",
"name": "Nomo:",
"location": "Loko:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Buscar:&nbsp;",
"name": "Nombre:",
"location": "Ubicación:",
"artist": "Artista:"
"artist": "Artista:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Rechercher :&nbsp;",
"name": "Nom :",
"location": "Lieu :",
"artist": "Artiste :"
"artist": "Artiste :",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Search:&nbsp;",
"name": "Name:",
"location": "Location:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Cerca :&nbsp;",
"name": "Nome :",
"location": "Luogo :",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "検索:",
"name": "名:",
"location": "場所:",
"artist": "作者:"
"artist": "作者:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "검색:&nbsp;",
"name": "이름:",
"location": "장소:",
"artist": "배지 디자이너:"
"artist": "배지 디자이너:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Szukaj:&nbsp;",
"name": "Nazwa:",
"location": "Lokalizacja:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Buscar:&nbsp;",
"name": "Nome:",
"location": "Localização:",
"artist": "Artista:"
"artist": "Artista:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/ro.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Caută:&nbsp;",
"name": "Nume:",
"location": "Locație:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Поиск:&nbsp;",
"name": "Название:",
"location": "Локация:",
"artist": "Художник:"
"artist": "Художник:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Ara:&nbsp;",
"name": "İsim:",
"location": "Konum:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/uk.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Пошук:&nbsp;",
"name": "Назва:",
"location": "Місце:",
"artist": "Artist:"
"artist": "Artist:",
"contents": "Text:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "Tìm kiếm:&nbsp;",
"name": "Tên:",
"location": "Địa điểm:",
"artist": "Họa sĩ:"
"artist": "Họa sĩ:",
"contents": "Nội dung:"
}
}
},
Expand Down
3 changes: 2 additions & 1 deletion lang/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,8 @@
"label": "搜索:&nbsp;",
"name": "名称:",
"location": "取得位置:",
"artist": "作者:"
"artist": "作者:",
"contents": "Text:"
}
}
},
Expand Down
Loading