您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a sorting and filters for fleets tabs
// ==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);