<style>
.hidden {
display: none !important;
}
label {
cursor: pointer;
}
/*пример стиля для тоггла*/
.toggle input {
display: none;
}
.toggle:has(input:checked) {
cursor: pointer;
background: #66bb6a;
color: var(--background);
}
.toggle {
padding: .25em;
border: 1px solid var(--border1);
}
</style>
<style>
#formoder {
display: none;
padding: 5px;
background: #ccc;
}
.ismoderator #formoder {
display: block;
}
</style>
<!-- Фильтры -->
<div id="len-pridumivat">
<div class="inner">
<h2>Список рас</h2>
<em>В списке рас кратко представлены расы персонажей, имеющихся на форуме, введённых в игру в качестве NPC
или описанных в заявках. Для поиска по фильтрам выберите нужные характеристики из представленных ниже.
Фильтр позволяет отсортировать список по трём основным категориям: территория, вид, отличительная черта.
Описание расы в списке дано в виде краткой характеристики по основным пунктам. С полным описанием расы
вы можете ознакомиться в анкете её представителя (доступны после регистрации).</em>
<div class="filter territory">
<h2>Территория</h2>
<em>Историческая родина расы (может не совпадать с местом фактического проживания
персонажа)<br>Поставьте галочку возле одной из трёх территорий, чтобы увидеть расы, проживающие во
всех системах выбранной территории. Для поиска по конкретной системе отметьте её в списке ниже.</em>
<div id="filt-block" class="imp">
<div class="checkbox-wrapper-1">
<h3>Нэсарская Империя</h3>
<div class="tooltip"> <img src="https://forumstatic.ru/files/001b/f2/fa/23616.png">
<span class="tooltiptext">Для вывода полного списка рас Империи поставьте отметку в поле
рядом. Для поиска по конкретной системе выберите её из списка внизу.</span>
</div>
<div class="round">
<input type="checkbox" id="checkbox-1">
<label for="checkbox-1"></label>
</div>
</div>
</div>
<div id="filt-block" class="fed">
<div class="checkbox-wrapper-2">
<h3>Объединенная Федерация</h3>
<div class="tooltip"> <img src="https://forumstatic.ru/files/001b/f2/fa/23616.png">
<span class="tooltiptext">Для вывода полного списка рас Федерации поставьте отметку в поле
рядом. Для поиска по конкретной системе выберите её из списка внизу.</span>
</div>
<div class="round">
<input type="checkbox" id="checkbox-2">
<label for="checkbox-2"></label>
</div>
</div>
</div>
<div id="filt-block" class="neu">
<div class="checkbox-wrapper-3">
<h3>Нейтральная территория</h3>
<div class="tooltip"> <img src="https://forumstatic.ru/files/001b/f2/fa/23616.png">
<span class="tooltiptext">Для вывода полного списка рас Нейтральной территории поставьте
отметку в поле рядом. Для поиска по конкретной системе выберите её из списка
внизу.</span>
</div>
<div class="round">
<input type="checkbox" id="checkbox-3">
<label for="checkbox-3"></label>
</div>
</div>
</div>
</div>
<div class="filter species">
<div id="filt-block" class="specieslist">
<h2>Виды</h2>
<em>Фильтр рас по видам. Подробнее о видах <a
href="https://codepen.io/starl1ng/pen/NWJBRME">здесь</a></em>
</div>
<div id="filt-block" class="uni">
<h2>Дополнительно</h2>
<em>Дополнительные теги фильтрации</em>
</div>
</div>
<div class="filter signs">
<h2>Отличительные черты</h2>
<em>Особенности каждой из рас. Названия расовых способностей из списка могут совпадать с названиями <a
href="https://codepen.io/starl1ng/pen/NWJBRME">пустотных способностей</a>. Расовые способности
присутствуют у каждого её представителя и не зависят от Пустоты. </em>
<details>
<summary>подробнее</summary>
<p>
</p>
</details>
</div>
</div>
<!-- Фильтры-конец -->
<!-- Блок для подгрузки данных -->
<div class="post-content not-found" style="" id="everspace"></div>
<div class="post-loader" style="display: none;">Загрузка...</div>
</div>
<script>
(() => {
const filterFieldToFilter = {
//виды
"grib": { name: "Грибовидные", type: "toggle", filterContainerSelector: '.species .specieslist' },
"gum": { name: "Гуманоиды", type: "toggle", filterContainerSelector: '.species .specieslist' },
"lit": { name: "Литоиды", type: "toggle", filterContainerSelector: '.species .specieslist' },
"mlek": { name: "Млекопитающие", type: "toggle", filterContainerSelector: '.species .specieslist' },
"okean": { name: "Океаниды", type: "toggle", filterContainerSelector: '.species .specieslist' },
"pern": { name: "Пернатые", type: "toggle", filterContainerSelector: '.species .specieslist' },
"rept": { name: "Рептилоиды", type: "toggle", filterContainerSelector: '.species .specieslist' },
"flor": { name: "Флориды", type: "toggle", filterContainerSelector: '.species .specieslist' },
"chlen": { name: "Членистоногие", type: "toggle", filterContainerSelector: '.species .specieslist' },
//дополнительные фильтры
//Империя
"alkura": { name: "Алькура", type: "toggle", filterContainerSelector: '.territory .imp' },
"alradif": { name: "Альрадиф", type: "toggle", filterContainerSelector: '.territory .imp' },
"aftab": { name: "Афтаб", type: "toggle", filterContainerSelector: '.territory .imp' },
"bcefeya": { name: "Бета Цефея", type: "toggle", filterContainerSelector: '.territory .imp' },
"gcefeyai": { name: "Гамма Цефея", type: "toggle", filterContainerSelector: '.territory .imp' },
"glanc": { name: "Гланц", type: "toggle", filterContainerSelector: '.territory .imp' },
"kalor": { name: "Калор", type: "toggle", filterContainerSelector: '.territory .imp' },
"kruger": { name: "Крюгер", type: "toggle", filterContainerSelector: '.territory .imp' },
"lyumena": { name: "Люмена", type: "toggle", filterContainerSelector: '.territory .imp' },
"persius": { name: "Персиус", type: "toggle", filterContainerSelector: '.territory .imp' },
"solus": { name: "Солус", type: "toggle", filterContainerSelector: '.territory .imp' },
"tondi": { name: "Тонди", type: "toggle", filterContainerSelector: '.territory .imp' },
"fernliht": { name: "Фернлихт", type: "toggle", filterContainerSelector: '.territory .imp' },
"fulgor": { name: "Фулгор", type: "toggle", filterContainerSelector: '.territory .imp' },
"helikait": { name: "Хеликайт", type: "toggle", filterContainerSelector: '.territory .imp' },
"eane": { name: "Эане", type: "toggle", filterContainerSelector: '.territory .imp' },
//Федерация
"alkindi": { name: "Аль-Кинди", type: "toggle", filterContainerSelector: '.territory .fed' },
"gcefeyaf": { name: "Гамма Цефея", type: "toggle", filterContainerSelector: '.territory .fed' },
"gerioz": { name: "Гериоз", type: "toggle", filterContainerSelector: '.territory .fed' },
"davinchi": { name: "Да Винчи", type: "toggle", filterContainerSelector: '.territory .fed' },
"delaks": { name: "Делакс", type: "toggle", filterContainerSelector: '.territory .fed' },
"djebtet": { name: "Джеб'тет", type: "toggle", filterContainerSelector: '.territory .fed' },
"zenkai": { name: "Зенкай", type: "toggle", filterContainerSelector: '.territory .fed' },
"zent": { name: "Зент", type: "toggle", filterContainerSelector: '.territory .fed' },
"irida": { name: "Ирида", type: "toggle", filterContainerSelector: '.territory .fed' },
"lavgud": { name: "Лавгуд", type: "toggle", filterContainerSelector: '.territory .fed' },
"laplas": { name: "Лаплас", type: "toggle", filterContainerSelector: '.territory .fed' },
"ozamis": { name: "Озамис", type: "toggle", filterContainerSelector: '.territory .fed' },
"olur": { name: "Олур", type: "toggle", filterContainerSelector: '.territory .fed' },
"rondell": { name: "Ронделл", type: "toggle", filterContainerSelector: '.territory .fed' },
"solsys": { name: "Солнечная система", type: "toggle", filterContainerSelector: '.territory .fed' },
"razmos": { name: "Размос", type: "toggle", filterContainerSelector: '.territory .fed' },
"enatos": { name: "Энатос", type: "toggle", filterContainerSelector: '.territory .fed' },
"yatun": { name: "Ятун", type: "toggle", filterContainerSelector: '.territory .fed' },
//Нейтральная территория
"sn1987a": { name: "SN 1987A", type: "toggle", filterContainerSelector: '.territory .neu' },
"aonxiii": { name: "Аон XIII", type: "toggle", filterContainerSelector: '.territory .neu' },
"befana": { name: "Бефана", type: "toggle", filterContainerSelector: '.territory .neu' },
"birham": { name: "Бирхам", type: "toggle", filterContainerSelector: '.territory .neu' },
"varkraim": { name: "Варкрайм", type: "toggle", filterContainerSelector: '.territory .neu' },
"kardos": { name: "Кардос", type: "toggle", filterContainerSelector: '.territory .neu' },
"klios": { name: "Клиос", type: "toggle", filterContainerSelector: '.territory .neu' },
"ksi": { name: "Кси", type: "toggle", filterContainerSelector: '.territory .neu' },
"lopsa": { name: "Лопса", type: "toggle", filterContainerSelector: '.territory .neu' },
"myucefeya": { name: "Мю Цефея", type: "toggle", filterContainerSelector: '.territory .neu' },
"noksiya": { name: "Ноксия", type: "toggle", filterContainerSelector: '.territory .neu' },
"praksima": { name: "Праксима", type: "toggle", filterContainerSelector: '.territory .neu' },
"proteya": { name: "Протея", type: "toggle", filterContainerSelector: '.territory .neu' },
"sovilo": { name: "Совило", type: "toggle", filterContainerSelector: '.territory .neu' },
//черты
"roga": { name: "Рога", type: "toggle", filterContainerSelector: '.signs' },
"text": { name: "Поиск по тексту", type: "search", filterValueGetter: (item) => item.innerText }
};
const config = {
idPrefix: "len-pridumivat",
emptyResultClass: "not-found",
itemsContainerClassname: "items",
itemClassname: "item",
filtersContainerClassname: "inner",
//Можно переопределить для всяких нестандартных штук в духе "фильтруем по содержанию определенного блока"
//valuesGetter: (item, field) => item.dataset.field,
valuesGetter: (item, field) => !!item.classList.contains(field),
//можно делать нестандартные обертки над итемами, например, спаны, если у нас только тогглы
createWrapper: () => document.createElement('span'),
//вызывается в конце обработчика change
afterFilterChange: (filterField, filterValue, filterElement) => {},
//вызывается когда фильтр засунут в обертку, а обертка засунута на свое место
afterFilterRender: (filterField, filterElement) => {}
};
//Код в который лучше без особых причин не лезть XD
const makeFiltersGreat = () => {
const {
idPrefix,
emptyResultClass,
itemsContainerClassname,
itemClassname,
filtersContainerClassname,
useUrlSearchParams,
valuesGetter,
createWrapper,
afterFilterChange,
afterFilterRender
} = config;
const filtersContainer = document.querySelector(`#${idPrefix} .${filtersContainerClassname}`);
const items = document.querySelectorAll(`#${idPrefix}${itemsContainerClassname ? (' .' + itemsContainerClassname) : ''} .${itemClassname}`);
const itemsForHideReasons = new Map();
items.forEach(item => itemsForHideReasons.set(item, new Set()));
const filtersConfigs = Object.entries(filterFieldToFilter);
const isSorted = filtersConfigs.reduce((acc, [field, { sortOrder }]) => {
if (sortOrder === undefined) return false;
return acc;
}, true);
if (isSorted) {
filtersConfigs.sort(([field1, { sortOrder: sortOrder1 }], [field2, { sortOrder: sortOrder2 }]) => sortOrder1 - sortOrder2);
}
filtersConfigs.forEach(([field, {
name, type, valueSeparator, filterContainerSelector, filterValueGetter, filterCreateWrapper, beautifyAfterWrapperFilled
}]) => {
const filterId = `${idPrefix}-filter-${field}`;
let filter;
//функция, которая принимает значение фильтра, значение итема и определяет, должен ли итем показываться
let valueComparator = (filterValue, value) => true;
const wrapper = filterCreateWrapper ? filterCreateWrapper() : createWrapper();
wrapper.id = `${filterId}-wrapper`;
wrapper.classList.add('wrapper');
if (type) wrapper.classList.add(type);
const label = document.createElement('label');
label.innerText = name;
label.htmlFor = filterId;
wrapper.append(label);
if (filterContainerSelector) {
document.querySelector(filterContainerSelector).append(wrapper)
} else filtersContainer.append(wrapper);
switch (type) {
case "search":
filter = document.createElement('input');
filter.type = 'text';
filter.addEventListener('input', (e) => {
const event = new Event('change')
e.target.dispatchEvent(event);
});
valueComparator = (filterValue, value) => {
return value.includes(filterValue);
}
break;
case "toggle":
filter = document.createElement('input');
filter.type = 'checkbox';
valueComparator = (filterValue, value) => {
const isFalseValue = !value && value !== '' || value === "false";
const isFalseFilterValue = filterValue === "false";
if (!filterValue || isFalseValue === isFalseFilterValue) return true
return false
}
break;
case "boolean":
filter = document.createElement('select');
filter.insertAdjacentHTML('afterbegin', `
<option label="Все"></option>
<option label="Да" value="true">Да</option>
<option label="Нет" value="false">Нет</option>
`);
valueComparator = (filterValue, value) => {
const isFalseValue = !value && value !== '' || value === "false";
const isFalseFilterValue = filterValue === "false";
if (!filterValue || isFalseValue === isFalseFilterValue) return true
return false
}
break;
default:
const values = new Set();
items.forEach(el => {
const vals = valueSeparator ? el.dataset[field].split(valueSeparator) : [el.dataset[field]];
vals.forEach(val => values.add(val));
});
filter = document.createElement('select');
filter.insertAdjacentHTML('afterbegin', '<option label="Все"></option>');
valueComparator = (filterValue, value) => {
if (!filterValue || filterValue === value) {
return true;
}
return false;
}
const valuesArray = Array.from(values);
valuesArray.sort();
for (const value of valuesArray) {
filter.insertAdjacentHTML('beforeend', `<option label="${value}" value="${value}">${value}</option>`);
}
}
wrapper.append(filter);
if (beautifyAfterWrapperFilled) beautifyAfterWrapperFilled(wrapper)
filter.id = filterId;
filter.addEventListener('change', (e) => {
const filterValue = e.target.type !== 'checkbox' ? e.target.value : e.target.checked;
if (useUrlSearchParams) {
const curentSearchParams = new URLSearchParams(location.search);
if (!filterValue) {
curentSearchParams.delete(field);
} else {
curentSearchParams.set(field, filterValue);
}
if (history.pushState) {
const newurl = location.origin + location.pathname + '?' + curentSearchParams.toString();
history.pushState({ path: newurl }, '', newurl);
}
}
let shownElementsCount = 0;
items.forEach(item => {
const rawValue = filterValueGetter ? filterValueGetter(item) : valuesGetter(item, field);
const values = valueSeparator ? rawValue?.split(valueSeparator) ?? [] : [rawValue];
const hideReasons = itemsForHideReasons.get(item);
const shouldBeShown = values.reduce((acc, cur) => {
return acc || valueComparator(filterValue, cur)
}, false);
if (shouldBeShown) {
hideReasons.delete(field);
if (!hideReasons.size) {
item.classList.remove("hidden");
shownElementsCount++;
}
} else {
hideReasons.add(field);
item.classList.add("hidden");
}
});
if (!shownElementsCount) {
document.querySelector(`#${idPrefix}`).classList.add(emptyResultClass);
}
afterFilterChange(field, filterValue, filter);
});
afterFilterRender(field, filter);
if (useUrlSearchParams) {
const searchParamValue = new URLSearchParams(location.search).get(field);
if (searchParamValue) {
const ev = new Event('change');
switch (type) {
case "toggle":
if (searchParamValue === 'true') {
filter.checked = true;
}
break;
case "search":
filter.value = searchParamValue;
}
filter.dispatchEvent(ev);
}
}
})
}
makeFiltersGreat();
})()
</script>
<script>
var posts = [3, 4, 8]
$.getJSON('/api.php?method=post.get&post_id=' + posts.join(','), function (jsondata) {
if (jsondata.response && (jsondata.response.length > 0)) {
$('.post-content').html(jsondata.response.map((el, index) => `<div id="formoder"><b>postId:</b> <a href="/viewtopic.php?id=${jsondata.response[index].topic_id}#p${posts[index]}">${posts[index]}</a></div>` + el.message).join('')).show();
$('.post-loader').hide();
}
else {
$('.post-loader').html('Ошибка загрузки');
}
});
</script>