Update shared-ip-manager.userscript.js
Added sort to usage
This commit is contained in:
parent
2733e86c8f
commit
2a267bcc05
@ -1,7 +1,7 @@
|
||||
// ==UserScript==
|
||||
// @name Shared IP Manager
|
||||
// @namespace http://tampermonkey.net/
|
||||
// @version 1.1.0
|
||||
// @version 1.1.1
|
||||
// @description Transform shared IP overlay into searchable table format
|
||||
// @author Ryahn
|
||||
// @match *://*/*
|
||||
@ -59,7 +59,7 @@
|
||||
|
||||
// Find all user entries
|
||||
const userEntries = overlay.querySelectorAll('.block-row.block-row--separated');
|
||||
|
||||
|
||||
if (userEntries.length === 0) return;
|
||||
|
||||
// Create table container
|
||||
@ -112,7 +112,7 @@
|
||||
transition: all 0.2s;
|
||||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
|
||||
// Add hover effect for button
|
||||
bannedFilterButton.addEventListener('mouseenter', function() {
|
||||
this.style.backgroundColor = '#dc2626';
|
||||
@ -166,47 +166,48 @@
|
||||
// Create table header
|
||||
const thead = document.createElement('thead');
|
||||
thead.style.cssText = `position: sticky; top: 0; z-index: 10;`;
|
||||
|
||||
|
||||
const headerRow = document.createElement('tr');
|
||||
headerRow.style.cssText = `background: #101113; border-bottom: 1px solid #101113; color: #959595;`;
|
||||
|
||||
|
||||
// Username header (sortable)
|
||||
const usernameHeader = document.createElement('th');
|
||||
usernameHeader.id = 'sort-username';
|
||||
usernameHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap; cursor: pointer; user-select: none;`;
|
||||
usernameHeader.innerHTML = `Username <span id="username-sort-icon" style="margin-left: 5px;">↕</span>`;
|
||||
|
||||
|
||||
// Join Date header
|
||||
const joinDateHeader = document.createElement('th');
|
||||
joinDateHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`;
|
||||
joinDateHeader.textContent = 'Join Date';
|
||||
|
||||
|
||||
// Messages header
|
||||
const messagesHeader = document.createElement('th');
|
||||
messagesHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`;
|
||||
messagesHeader.textContent = 'Messages';
|
||||
|
||||
|
||||
// Country header
|
||||
const countryHeader = document.createElement('th');
|
||||
countryHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`;
|
||||
countryHeader.textContent = 'Country';
|
||||
|
||||
|
||||
// Provider header
|
||||
const providerHeader = document.createElement('th');
|
||||
providerHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap;`;
|
||||
providerHeader.textContent = 'Provider';
|
||||
|
||||
|
||||
// Type header (sortable)
|
||||
const typeHeader = document.createElement('th');
|
||||
typeHeader.id = 'sort-type';
|
||||
typeHeader.style.cssText = `padding: 10px 12px; text-align: left; border-right: 1px solid #17191b; font-weight: 600; color: #959595; white-space: nowrap; cursor: pointer; user-select: none;`;
|
||||
typeHeader.innerHTML = `Type <span id="type-sort-icon" style="margin-left: 5px;">↕</span>`;
|
||||
|
||||
// Usage header
|
||||
|
||||
// Usage header (sortable)
|
||||
const usageHeader = document.createElement('th');
|
||||
usageHeader.style.cssText = `padding: 10px 12px; text-align: left; font-weight: 600; color: #959595; white-space: nowrap;`;
|
||||
usageHeader.textContent = 'Usage';
|
||||
|
||||
usageHeader.id = 'sort-usage';
|
||||
usageHeader.style.cssText = `padding: 10px 12px; text-align: left; font-weight: 600; color: #959595; white-space: nowrap; cursor: pointer; user-select: none;`;
|
||||
usageHeader.innerHTML = `Usage <span id="usage-sort-icon" style="margin-left: 5px;">↕</span>`;
|
||||
|
||||
// Append all headers to row
|
||||
headerRow.appendChild(usernameHeader);
|
||||
headerRow.appendChild(joinDateHeader);
|
||||
@ -215,12 +216,12 @@
|
||||
headerRow.appendChild(providerHeader);
|
||||
headerRow.appendChild(typeHeader);
|
||||
headerRow.appendChild(usageHeader);
|
||||
|
||||
|
||||
thead.appendChild(headerRow);
|
||||
|
||||
// Create table body
|
||||
const tbody = document.createElement('tbody');
|
||||
|
||||
|
||||
// Process each user entry
|
||||
userEntries.forEach((entry, index) => {
|
||||
const row = document.createElement('tr');
|
||||
@ -234,15 +235,15 @@
|
||||
const usernameLink = entry.querySelector('h3.contentRow-header a.username');
|
||||
const username = usernameLink ? usernameLink.textContent.trim() : 'N/A';
|
||||
const profileUrl = usernameLink ? usernameLink.href : '#';
|
||||
|
||||
|
||||
// Check if user is banned
|
||||
const usernameSpan = entry.querySelector('h3.contentRow-header a.username span');
|
||||
const isBanned = usernameSpan && usernameSpan.classList.contains('username--banned');
|
||||
|
||||
|
||||
// Join date
|
||||
const joinDateElement = entry.querySelector('time.u-dt');
|
||||
const joinDate = joinDateElement ? joinDateElement.textContent.trim() : 'N/A';
|
||||
|
||||
|
||||
// Messages
|
||||
const dtElements = entry.querySelectorAll('dt');
|
||||
let messagesElement = null;
|
||||
@ -252,23 +253,23 @@
|
||||
break;
|
||||
}
|
||||
}
|
||||
const messages = messagesElement ?
|
||||
const messages = messagesElement ?
|
||||
(messagesElement.nextElementSibling ? messagesElement.nextElementSibling.textContent.trim() : '0') : '0';
|
||||
|
||||
|
||||
// Country flag and name
|
||||
const flagElement = entry.querySelector('.tck-provider-country-flag');
|
||||
const countryCode = flagElement ? getCountryCode(flagElement) : '';
|
||||
const countryName = flagElement ? getCountryName(flagElement) : '';
|
||||
const flagEmoji = getFlagEmoji(countryCode);
|
||||
|
||||
|
||||
// Provider info
|
||||
const providerElement = entry.querySelector('.tck-provider-txt');
|
||||
const providerInfo = providerElement ? getProviderInfo(providerElement) : { name: 'N/A', asn: '' };
|
||||
|
||||
|
||||
// Type info
|
||||
const typeElement = entry.querySelector('.tck-provider-type');
|
||||
const typeInfo = typeElement ? getTypeInfo(typeElement) : 'N/A';
|
||||
|
||||
|
||||
// Usage count
|
||||
const liElements = entry.querySelectorAll('li');
|
||||
let usageElement = null;
|
||||
@ -280,8 +281,8 @@
|
||||
}
|
||||
const usageText = usageElement ? usageElement.textContent.trim() : '0 time';
|
||||
const usageCount = getUsageCount(usageText);
|
||||
|
||||
|
||||
|
||||
|
||||
// Build row HTML
|
||||
row.innerHTML = `
|
||||
<td style="padding: 10px 12px; border-right: 1px solid #e5e7eb; white-space: nowrap;">
|
||||
@ -319,31 +320,31 @@
|
||||
|
||||
// Filter state
|
||||
let showBannedOnly = false;
|
||||
|
||||
|
||||
// Function to apply both search and filter
|
||||
function applyFilters() {
|
||||
const searchTerm = searchInput.value.toLowerCase();
|
||||
const rows = tbody.querySelectorAll('tr');
|
||||
let visibleCount = 0;
|
||||
let bannedCount = 0;
|
||||
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
const isBanned = row.querySelector('a[href*="/members/"]') &&
|
||||
const isBanned = row.querySelector('a[href*="/members/"]') &&
|
||||
row.querySelector('a[href*="/members/"]').textContent.includes('🚫');
|
||||
|
||||
|
||||
const matchesSearch = text.includes(searchTerm);
|
||||
const matchesFilter = !showBannedOnly || isBanned;
|
||||
|
||||
|
||||
const shouldShow = matchesSearch && matchesFilter;
|
||||
row.style.display = shouldShow ? '' : 'none';
|
||||
|
||||
|
||||
if (shouldShow) {
|
||||
visibleCount++;
|
||||
if (isBanned) bannedCount++;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Update count display
|
||||
if (showBannedOnly) {
|
||||
countDisplay.textContent = `Showing ${bannedCount} banned users`;
|
||||
@ -351,14 +352,14 @@
|
||||
countDisplay.textContent = `${visibleCount} users (${bannedCount} banned)`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Add search functionality
|
||||
searchInput.addEventListener('input', applyFilters);
|
||||
|
||||
|
||||
// Add banned filter functionality
|
||||
bannedFilterButton.addEventListener('click', () => {
|
||||
showBannedOnly = !showBannedOnly;
|
||||
|
||||
|
||||
if (showBannedOnly) {
|
||||
bannedFilterButton.textContent = 'Show All Users';
|
||||
bannedFilterButton.classList.add('active');
|
||||
@ -370,7 +371,7 @@
|
||||
bannedFilterButton.style.backgroundColor = '#dc262620';
|
||||
bannedFilterButton.style.color = '#dc2626';
|
||||
}
|
||||
|
||||
|
||||
applyFilters();
|
||||
});
|
||||
|
||||
@ -380,31 +381,31 @@
|
||||
// Assemble table
|
||||
table.appendChild(thead);
|
||||
table.appendChild(tbody);
|
||||
|
||||
|
||||
// Add table to wrapper
|
||||
tableWrapper.appendChild(table);
|
||||
|
||||
|
||||
// Add elements to container
|
||||
tableContainer.appendChild(searchFilterContainer);
|
||||
tableContainer.appendChild(tableWrapper);
|
||||
|
||||
// Sorting functionality
|
||||
let currentSort = { column: null, direction: 'asc' };
|
||||
|
||||
|
||||
function sortTable(column, direction) {
|
||||
console.log('Sorting by:', column, direction); // Debug log
|
||||
|
||||
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
console.log('Found rows:', rows.length); // Debug log
|
||||
|
||||
|
||||
if (rows.length === 0) {
|
||||
console.log('No rows to sort');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
rows.sort((a, b) => {
|
||||
let aValue, bValue;
|
||||
|
||||
|
||||
if (column === 'username') {
|
||||
const aLink = a.querySelector('a');
|
||||
const bLink = b.querySelector('a');
|
||||
@ -413,34 +414,48 @@
|
||||
} else if (column === 'type') {
|
||||
aValue = a.cells[5] ? a.cells[5].textContent.trim().toLowerCase() : '';
|
||||
bValue = b.cells[5] ? b.cells[5].textContent.trim().toLowerCase() : '';
|
||||
} else if (column === 'usage') {
|
||||
// Parse usage as numbers for proper numerical sorting
|
||||
aValue = parseFloat(a.cells[6] ? a.cells[6].textContent.trim() : '0') || 0;
|
||||
bValue = parseFloat(b.cells[6] ? b.cells[6].textContent.trim() : '0') || 0;
|
||||
}
|
||||
|
||||
|
||||
console.log('Comparing:', aValue, 'vs', bValue); // Debug log
|
||||
|
||||
if (direction === 'asc') {
|
||||
return aValue.localeCompare(bValue);
|
||||
|
||||
if (column === 'usage') {
|
||||
// Numerical comparison for usage
|
||||
if (direction === 'asc') {
|
||||
return aValue - bValue;
|
||||
} else {
|
||||
return bValue - aValue;
|
||||
}
|
||||
} else {
|
||||
return bValue.localeCompare(aValue);
|
||||
// String comparison for username and type
|
||||
if (direction === 'asc') {
|
||||
return aValue.localeCompare(bValue);
|
||||
} else {
|
||||
return bValue.localeCompare(aValue);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Clear tbody and re-append sorted rows
|
||||
tbody.innerHTML = '';
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
|
||||
|
||||
// Update sort icons
|
||||
document.querySelectorAll('[id$="-sort-icon"]').forEach(icon => {
|
||||
icon.textContent = '↕';
|
||||
});
|
||||
|
||||
|
||||
const sortIcon = document.getElementById(`${column}-sort-icon`);
|
||||
if (sortIcon) {
|
||||
sortIcon.textContent = direction === 'asc' ? '↑' : '↓';
|
||||
}
|
||||
|
||||
|
||||
console.log('Sorting completed'); // Debug log
|
||||
}
|
||||
|
||||
|
||||
// Add click event listeners for sortable columns
|
||||
console.log('Adding click listener to username header');
|
||||
usernameHeader.addEventListener('click', (e) => {
|
||||
@ -455,7 +470,7 @@
|
||||
console.log('Current sort:', currentSort);
|
||||
sortTable(currentSort.column, currentSort.direction);
|
||||
});
|
||||
|
||||
|
||||
// Add hover effects for username header
|
||||
usernameHeader.addEventListener('mouseenter', function() {
|
||||
this.style.backgroundColor = '#1a1a1a';
|
||||
@ -463,7 +478,7 @@
|
||||
usernameHeader.addEventListener('mouseleave', function() {
|
||||
this.style.backgroundColor = '#101113';
|
||||
});
|
||||
|
||||
|
||||
console.log('Adding click listener to type header');
|
||||
typeHeader.addEventListener('click', (e) => {
|
||||
console.log('Type header clicked');
|
||||
@ -477,7 +492,7 @@
|
||||
console.log('Current sort:', currentSort);
|
||||
sortTable(currentSort.column, currentSort.direction);
|
||||
});
|
||||
|
||||
|
||||
// Add hover effects for type header
|
||||
typeHeader.addEventListener('mouseenter', function() {
|
||||
this.style.backgroundColor = '#1a1a1a';
|
||||
@ -486,6 +501,28 @@
|
||||
this.style.backgroundColor = '#101113';
|
||||
});
|
||||
|
||||
console.log('Adding click listener to usage header');
|
||||
usageHeader.addEventListener('click', (e) => {
|
||||
console.log('Usage header clicked');
|
||||
e.preventDefault();
|
||||
if (currentSort.column === 'usage') {
|
||||
currentSort.direction = currentSort.direction === 'asc' ? 'desc' : 'asc';
|
||||
} else {
|
||||
currentSort.column = 'usage';
|
||||
currentSort.direction = 'asc';
|
||||
}
|
||||
console.log('Current sort:', currentSort);
|
||||
sortTable(currentSort.column, currentSort.direction);
|
||||
});
|
||||
|
||||
// Add hover effects for usage header
|
||||
usageHeader.addEventListener('mouseenter', function() {
|
||||
this.style.backgroundColor = '#1a1a1a';
|
||||
});
|
||||
usageHeader.addEventListener('mouseleave', function() {
|
||||
this.style.backgroundColor = '#101113';
|
||||
});
|
||||
|
||||
// Replace overlay content
|
||||
overlay.innerHTML = '';
|
||||
overlay.appendChild(tableContainer);
|
||||
@ -507,7 +544,7 @@
|
||||
|
||||
// Also listen for overlay events in case it loads dynamically
|
||||
document.addEventListener('click', (e) => {
|
||||
if (e.target.classList.contains('js-overlayClose') ||
|
||||
if (e.target.classList.contains('js-overlayClose') ||
|
||||
e.target.closest('.js-overlayClose')) {
|
||||
// Overlay is closing, wait for it to reopen
|
||||
setTimeout(waitForOverlay, 500);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user