diff --git a/shared-ip-manager.userscript.js b/shared-ip-manager.userscript.js index 2ebd510..cc94885 100644 --- a/shared-ip-manager.userscript.js +++ b/shared-ip-manager.userscript.js @@ -1,7 +1,7 @@ // ==UserScript== // @name Shared IP Manager // @namespace http://tampermonkey.net/ -// @version 1.1.1 +// @version 1.1.2 // @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,48 +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 `; - + // 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 `; - + // Usage header (sortable) const usageHeader = document.createElement('th'); 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 `; - + // Append all headers to row headerRow.appendChild(usernameHeader); headerRow.appendChild(joinDateHeader); @@ -216,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'); @@ -235,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; @@ -253,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; @@ -281,8 +281,8 @@ } const usageText = usageElement ? usageElement.textContent.trim() : '0 time'; const usageCount = getUsageCount(usageText); - - + + // Build row HTML row.innerHTML = ` @@ -296,7 +296,7 @@ ${flagEmoji} ${countryCode} - + ${providerInfo.name} @@ -320,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`; @@ -352,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'); @@ -371,7 +371,7 @@ bannedFilterButton.style.backgroundColor = '#dc262620'; bannedFilterButton.style.color = '#dc2626'; } - + applyFilters(); }); @@ -381,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'); @@ -419,9 +419,9 @@ 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 (column === 'usage') { // Numerical comparison for usage if (direction === 'asc') { @@ -438,24 +438,24 @@ } } }); - + // 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) => { @@ -470,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'; @@ -478,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'); @@ -492,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'; @@ -500,7 +500,7 @@ typeHeader.addEventListener('mouseleave', function() { this.style.backgroundColor = '#101113'; }); - + console.log('Adding click listener to usage header'); usageHeader.addEventListener('click', (e) => { console.log('Usage header clicked'); @@ -514,7 +514,7 @@ console.log('Current sort:', currentSort); sortTable(currentSort.column, currentSort.direction); }); - + // Add hover effects for usage header usageHeader.addEventListener('mouseenter', function() { this.style.backgroundColor = '#1a1a1a'; @@ -544,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);