// ==UserScript==
// @name Spacom.Addons.Fleets.Sort
// @version 0.1.4
// @namespace http://dimio.org/
// @description Add a sorting and filters for fleets tabs
// @author dimio ([email protected])
// @license MIT
// @homepage https://github.com/dimio/userscripts-spacom.ru-addons
// @supportURL https://github.com/dimio/userscripts-spacom.ru-addons/issues
// @supportURL https://spacom.ru/forum/discussion/47/polzovatelskie-skripty
// @encoding utf-8
// @match http*://spacom.ru/?act=game/map*
// @include http*://spacom.ru/?act=game/map*
// @run-at document-end
// ==/UserScript==
console.log(GM_info.script.name, 'booted v.', GM_info.script.version);
const homePage = GM_info.scriptMetaStr.split('\n')[6].split(' ')[6];
const ERR_MSG = {
NO_LIB: `Для работы ${GM_info.script.name} необходимо установить и включить последние версии следующих дополнений:
<ul>
<li>Spacom.Addons</li>
<li>Spacom.Addons.Fleets.Common</li>
</ul>
<a href="${homePage}">${homePage}</a>`,
NO_FILTER_PARAMS: `Не найдены параметры для фильтрации.
Для сброса фильтров закройте и откройте заново текущую вкладку флотов.`,
NO_FILTER_SELECTED: `Условия фильтрации не указаны.`,
};
(function (window) {
'use strict';
window.unsafeWindow = window.unsafeWindow || window;
const w = unsafeWindow;
const Addons = w.Addons;
if (w.self !== w.top) {
return;
}
if (!Addons) {
// if (!Addons || !Addons.Fleets.Common) {
w.showSmallMessage(ERR_MSG.NO_LIB);
return;
}
function Filter(by, isExclude = false) {
this.by = by;
this.isExclude = isExclude;
}
Addons.Fleets.Sort = {
/**
* contains current fleets by fleet tab
* note: use Proxy or Object.observe for watch to map.fleets changes
* and cache filtered fleets for each tab?
*/
fleets: [],
filters: {},
sort: {},
garrisonIco: (Addons.Fleets.Common) ? Addons.Fleets.Common.garrisonIco : "5.png",
toggleSubMenu(owner, fleetType, redraw) {
const subMenu = `fleets_${owner}_${fleetType}`;
// Current Fleets tab was closed, purge filters & drop filtered fleets for current tab
if (w.sub_menu === subMenu && !redraw) {
this.clearFilters(subMenu);
return false;
}
return subMenu;
},
createDefaultFilters(owner, fleetType) {
if (!Addons.Common.isVariableDefined(this.filters[w.sub_menu])) {
this.filters[w.sub_menu] = [
new Filter({'type': [fleetType], 'owner': [owner]}),
new Filter({'weight': ["0"]}, true)
];
}
if (!Addons.Common.isVariableDefined(this.sort[w.sub_menu])) {
this.sort[w.sub_menu] = {};
}
},
clearFilters(subMenu) {
delete this.filters[subMenu];
delete this.sort[subMenu];
},
showFleets(opt) {
const owner = opt.owner;
const fleetType = opt.fleetType || 'fleet';
const sortBy = opt.sortBy || 'weight';
let redraw = opt.redraw || false; //true for sort & filter buttons
w.map.clearInfo();
if ((w.sub_menu = this.toggleSubMenu(owner, fleetType, redraw)) === false) {
return false;
}
this.createDefaultFilters(owner, fleetType);
this.fleets = w.map.fleets.slice();
this.fleets.forEach(fleet => {
fleet['type'] = this.getFleetType(fleet);
// hack for sorting by ship type (== ship icon) for garrisons
fleet.ico = this.setDummyGarrisonIco(fleet);
});
// apply filters for current fleets tab
this.filters[w.sub_menu].forEach(filter => {
this.fleets = this.filterBy(this.fleets, filter);
});
// apply sorting for current fleets tab
this.sortFleets(owner, sortBy, redraw);
// show fleets for current fleets tab
this.drawFleetsTab(this.fleets);
// add sorting & filtering buttons to current fleets tab
this.addButtons(owner, fleetType);
// add mark/unmark buttons
if (Addons.Fleets.MarkOnMap) {
Addons.Fleets.MarkOnMap.init();
}
// add summary button
if (Addons.Fleets.Summary) {
Addons.Fleets.Summary.init();
}
return true;
},
showFilteredFleets(owner, fleetType, filterBy) {
const filterParams = this.getFilterParams(filterBy);
if (Addons.Common.isObjNotEmpty(filterParams)) {
this.getFilter(filterParams, filterBy)
.then(
filter => {
this.filters[w.sub_menu].push(filter);
this.showFleets({
'owner': owner,
'fleetType': fleetType,
'redraw': true,
})
},
error => w.showSmallMessage(ERR_MSG.NO_FILTER_SELECTED)
);
}
else {
w.showSmallMessage(ERR_MSG.NO_FILTER_PARAMS);
}
},
getFilterParams(filterBy) {
let filterParams = {};
this.fleets.forEach(fleet => {
filterParams[this.getFilterParam(filterBy, fleet)] = fleet[filterBy];
});
filterParams = Object.fromEntries(
Object.entries(filterParams).sort((a, b) => {
return Addons.Sort.alphabetically(a[0], b[0])
})
);
return filterParams;
},
getFilterParam(filterBy, fleet) {
if (filterBy === 'star_id') {
const star = w.map.stars[fleet[filterBy]];
return star.name + ' ' + star.x + ':' + star.y;
}
return fleet[filterBy];
},
getFilter(params, filterBy) {
const isExcludeId = 'filtering-list-exclude';
const filter = new Filter({[filterBy]: []});
let message = `Отфильтровать по:</br>
<select id='fl_filter' size='
${Object.keys(params).length < 8 ? Object.keys(params).length + 1 : 8}'
multiple='multiple'>`;
for (const i in params) {
if (params.hasOwnProperty(i)) {
message += `<option value="${params[i]}">${i}</option>`;
}
}
message += '</select></br>';
message += `<input type="checkbox" id="${isExcludeId}"/>`;
message += `<label for="${isExcludeId}">Исключить выбранное</label>`;
w.showSmallMessage(message);
$(`#${isExcludeId}`).change(function () {
filter.isExclude = $(this).is(':checked');
});
$('#fl_filter').change(function () {
filter.by[filterBy] = $(this).val();
});
return new Promise(function (resolve, reject) {
$('#data_modal > button').removeAttr("onclick")
.click(function () {
$('#fl_filter').change();
$.modal.close();
Addons.Common.isVariableDefined(filter.by[filterBy])
? resolve(filter) : reject();
});
});
},
sortFleets(owner, sortBy, redraw) {
if (Addons.Common.isVariableDefined(this.sort[w.sub_menu]["last"])
&& (!redraw || sortBy === 'weight')) {
this.sortBy(this.fleets, this.sort[w.sub_menu]["last"], owner);
}
else {
this.sortBy(this.fleets, sortBy, owner);
if (sortBy === this.sort[w.sub_menu]["last"]) {
this.sort[w.sub_menu]["isReverse"] = !this.sort[w.sub_menu]["isReverse"];
}
this.sort[w.sub_menu]["last"] = (sortBy === 'weight') ? undefined : sortBy;
}
if (this.sort[w.sub_menu]["isReverse"]) {
this.fleets.reverse();
}
},
getFleetType(fleet) {
/** non-own garrisons have a `fleet.garrison: "0"`
* own garrisons have a `fleet.garrison: "1"`
* all garrisons name is `fleet.fleet_name: "Гарнизон"`
* and ico is `fleet.ico: null`
*/
return (+fleet.garrison === 0) ? 'fleet' : 'garrison';
},
setDummyGarrisonIco(fleet) {
// hack for sorting by ship type (== ship icon)
return (fleet.ico !== null) ? fleet.ico : this.garrisonIco;
},
filterBy(fleets, filter) {
if (Addons.Common.isObjNotEmpty(fleets)) {
const keys = Object.keys(filter.by);
const values = Object.values(filter.by);
return fleets.filter(fleet => {
// it's a dark magic :)
return keys.every((key, index) => {
return filter.isExclude ?
values[index].every((value) => {
return fleet[key] !== value
})
:
values[index].some((value) => {
return fleet[key] === value
})
})
// && +fleet.weight !== 0;
});
}
return [];
},
sortBy(fleets, sortBy, owner) {
switch (sortBy) {
case 'weight':
fleets.sort(w.fleetOrder);
break;
case 'fleet_speed':
fleets.sort(this.sorter.speed);
fleets.map((fleet) => {
fleet.fleet_speed = parseFloat(fleet.fleet_speed).toFixed(2);
return fleet;
});
break;
case 'turn': // by fleet state
if (owner !== 'own') {
fleets.sort(this.sorter.state.other);
}
else {
fleets.sort(this.sorter.own).reverse();
}
break;
case 'ico': // by fleet type
fleets.sort(this.sorter.type);
break;
case 'fleet_name': // by owner & fleet names
if (owner === 'own') {
fleets.sort(this.sorter.fleetName);
break;
}
fleets.sort(this.sorter.fleetName);
fleets.sort(this.sorter.playerName);
break;
case 'stat':
fleets.sort(this.sorter.combatPower.hp);
// fleets.sort(this.sorter.combatPower.health);
fleets.reverse();
break;
case 'player_id':
if (owner !== 'other') {
break;
}
fleets.sort(this.sorter.playerId);
break;
default:
fleets.sort(w.fleetOrder);
break;
}
// sort in-place
// return fleets;
},
drawFleetsTab(fleets) {
if (fleets && Addons.Common.isObjNotEmpty(fleets)) {
$('#items_list').append(w.tmpl('fleets_title', fleets));
for (const fleet of fleets) {
if (Addons.Common.isVariableDefined(fleet)) {
w.map.showBlockFleet(fleet, fleet.owner);
}
}
$('#items_list>>>[title],#items_list>>>>[title]').qtip({
position: {
my: 'bottom center', // at the bottom right of...
at: 'top center', // Position my top left...
},
style: {
classes: 'qtip-dark tips',
},
});
}
else {
$('#items_list').html(
'<div class="player_fleet_title">Нет подходящих флотов</div>');
}
},
getNaviDivs() {
const divs = {};
divs.fleet_speed = $('#items_list .fleet_speed')[0];
divs.fleet_name = $('#items_list .fleet_name')[0];
divs.turn = $('#items_list .fleet_state')[0];
divs.ico = $('#items_list .fleet_ico_list')[0];
divs.stat = $('#items_list .fleet_stat')[0];
return divs;
},
addButtons(owner, fleetType) {
const timerID = setInterval(() => {
const divs = this.getNaviDivs();
if (Addons.Common.isObjNotEmpty(divs)) {
clearInterval(timerID);
this.addSortButtons(divs, owner, fleetType);
this.addFilterButtons(divs, owner, fleetType);
}
}, 0);
},
addSortButtons(divs, owner, fleetType) {
// div match a sortBy
for (let div in divs) {
if (divs.hasOwnProperty(div) && Addons.Common.isVariableDefined(divs[div])) {
if (fleetType === "garrison") {
if (div !== "ico" && div !== "stat") {
continue;
}
}
Addons.DOM.makeClickable({
elem: divs[div],
icon: 'fa-sort',
css_name: `sort-by-${div}`,
title: 'Отсортировать',
cb: `Addons.Fleets.Sort.showFleets({owner:'${owner}',fleetType:'${fleetType}',sortBy:'${div}',redraw:true})`
});
}
}
},
addFilterButtons(divs, owner, fleetType) {
// div match a filterBy
for (let div in divs) {
if (divs.hasOwnProperty(div) && Addons.Common.isVariableDefined(divs[div])) {
if (div === 'fleet_speed' || div === 'stat') {
continue;
}
if (fleetType === 'garrison' &&
(div === 'turn' || div === 'fleet_name')) {
continue;
}
if ((owner === 'other' || owner === 'peace') &&
div === 'fleet_name') {
// add the additional button before current
Addons.DOM.appendClickableIcon({
elem: divs[div],
icon: 'fa-id-badge',
css_name: `filter-by-${div}`,
title: 'Отфильтровать по владельцу',
cb: `Addons.Fleets.Sort.showFilteredFleets('${owner}','${fleetType}','player_name')`
});
}
if (div === 'turn') {
// add the additional button before current
Addons.DOM.appendClickableIcon({
elem: divs[div],
icon: 'fa-crosshairs',
css_name: `filter-by-star_id`,
title: 'Отфильтровать по системе',
cb: `Addons.Fleets.Sort.showFilteredFleets('${owner}','${fleetType}','star_id')`
});
}
Addons.DOM.appendClickableIcon({
elem: divs[div],
icon: 'fa-filter',
css_name: `filter-by-${div}`,
title: 'Отфильтровать',
cb: `Addons.Fleets.Sort.showFilteredFleets('${owner}','${fleetType}','${div}')`
});
}
}
},
init() {
$('#navi > div:nth-child(2)').attr('onclick',
'Addons.Fleets.Sort.showFleets({owner:\'own\'});return false;');
$('#navi > div:nth-child(3)').attr('onclick',
'Addons.Fleets.Sort.showFleets({owner:\'other\'});return false;');
Addons.DOM.createNaviBarButton('Гарнизон', 1,
'Addons.Fleets.Sort.showFleets({owner:\'own\',fleetType: \'garrison\'});return false;');
Addons.DOM.createNaviBarButton('Союзные', 3,
'Addons.Fleets.Sort.showFleets({owner:\'peace\'});return false;');
Addons.DOM.createNaviBarButton('Пираты', 5,
'Addons.Fleets.Sort.showFleets({owner:\'pirate\'});return false;');
},
sorter: {
state: {
own(a, b) {
if (a.allow_explore > b.allow_explore) {
return 1;
}
else if (a.allow_explore < b.allow_explore) {
return -1;
}
if (a.allow_fly > b.allow_fly) {
return 1;
}
else if (a.allow_fly < b.allow_fly) {
return -1;
}
if (a.allow_transfer > b.allow_transfer) {
return 1;
}
else if (a.allow_transfer < b.allow_transfer) {
return -1;
}
if (a.allow_garrison > b.allow_garrison) {
return 1;
}
else if (a.allow_garrison < b.allow_garrison) {
return -1;
}
if (a.allow_station > b.allow_station) {
return -1; // изучает аномалию - опускаем ниже
}
else if (a.allow_station < b.allow_station) {
return 1; // готов изучить или не станция - поднять
}
if (a.start_turn > b.start_turn) {
return -1; // будет дольше в полёте - опустить
}
else if (a.start_turn < b.start_turn) {
return 1;
}
return 0;
},
other(a, b) {
return Addons.Sort.numerically(a.turn, b.turn);
},
},
combatPower: {
health(a, b) {
return Addons.Sort.numerically(a.health, b.health);
},
hp(a, b) {
return Addons.Sort.numerically(
a.hp + a.laser_hp,
b.hp + b.laser_hp
);
},
},
speed(a, b) {
// order desc
return -Addons.Sort.numerically(a.fleet_speed, b.fleet_speed);
},
type(a, b) {
return Addons.Sort.alphabetically(a.ico, b.ico);
},
fleetName(a, b) {
return Addons.Sort.alphabetically(a.fleet_name, b.fleet_name);
},
playerName(a, b) {
return Addons.Sort.alphabetically(a.player_name, b.player_name);
},
playerId(a, b) {
// order asc
return Addons.Sort.numerically(a.player_id, b.player_id);
},
},
};
Addons.Fleets.Sort.init(this);
})(window);