Calculator:HighLite/Nameplates

Revision as of 19:12, 18 July 2025 by Ryan (talk | contribs)

HighLite Nameplate Calculator

`; // Assign elements to global variables AFTER they are injected itemSearchInput = document.getElementById('item-search'); outputListTextarea = document.getElementById('output-list'); copyButton = document.getElementById('copy-button'); copyFeedback = document.getElementById('copy-feedback'); itemDisplayTable = document.getElementById('item-display-table'); // Get reference to the table on the main page } /** * Initializes the interactive buttons and state for each item in the table. */ function initializeItemRows() { const loadingIndicator = document.getElementById('loading-indicator'); const errorMessage = document.getElementById('error-message'); const calculatorControls = document.getElementById('calculator-controls'); if (!itemDisplayTable || !itemDisplayTable.tBodies[0] || itemDisplayTable.tBodies[0].rows.length === 0) { errorMessage.textContent = 'Error: Item data table not found or empty. Please ensure your Cargo query is correct and populating the table correctly.'; errorMessage.style.display = 'block'; if (loadingIndicator) loadingIndicator.style.display = 'none'; console.error('Item display table (ID: item-display-table) not found or has no tbody/rows after waiting.'); return; } // Store the tbody element once it's confirmed to exist tableTbodyElement = itemDisplayTable.tBodies[0]; const rows = Array.from(tableTbodyElement.rows); // Convert to array for easier manipulation // Temporary map to hold consolidated items and their determined state // Key: consolidated item name, Value: { name, state, rowElement (primary one), buttonsCell (primary one) } const consolidatedItemsMap = new Map(); rows.forEach(row => { const itemNameLinkCell = row.cells[1]; const itemNameLink = itemNameLinkCell ? itemNameLinkCell.querySelector('a') : null; const originalItemName = itemNameLink ? itemNameLink.textContent.trim() : ''; if (!originalItemName) { console.warn('Skipping row due to missing original item name:', row); row.style.display = 'none'; // Hide rows without a name return; } const consolidatedName = getConsolidatedItemName(originalItemName); const buttonsCell = row.querySelector('.nameplate-buttons-cell'); if (!buttonsCell) { console.warn('Skipping row due to missing buttons cell:', row); row.style.display = 'none'; // Hide rows without a buttons cell return; } // Check if we've already processed this consolidated item if (!consolidatedItemsMap.has(consolidatedName)) { // This is the first time we see this consolidated item name consolidatedItemsMap.set(consolidatedName, { name: consolidatedName, state: -1, // Default to hide initially, will be overridden by DEFAULT_ITEM_LIST rowElement: row, // This row will be the primary one for this consolidated item buttonsCell: buttonsCell // This cell will host the buttons }); // Update the displayed item name in the table if (itemNameLink) { itemNameLink.textContent = consolidatedName; } else { // Fallback if no link, though usually there should be one itemNameLinkCell.textContent = consolidatedName; } // Create and append buttons to this consolidated item's primary buttonsCell const buttonsContainer = document.createElement('div'); buttonsContainer.className = 'item-status-buttons'; buttonsContainer.dataset.itemName = consolidatedName; // For event delegation const states = [ { value: -1, text: 'Hide' }, { value: 0, text: 'Default' }, { value: 1, text: 'Priority' } ]; states.forEach(state => { const button = document.createElement('button'); button.textContent = state.text; button.dataset.value = state.value; buttonsContainer.appendChild(button); }); buttonsCell.appendChild(buttonsContainer); } else { // This is a duplicate of a consolidated item, so hide its row row.style.display = 'none'; // And remove any buttons that might have been automatically rendered by Cargo for it if (buttonsCell) { buttonsCell.innerHTML = ''; // Clear contents of the button cell } } }); // Convert the map values to an array for allItems allItems = Array.from(consolidatedItemsMap.values()); // Apply the default list or any saved state to the consolidated items parseAndApplyList(DEFAULT_ITEM_LIST); // This will update the 'state' in allItems generateOutputList(); // Initial generation of the output list if (loadingIndicator) loadingIndicator.style.display = 'none'; if (calculatorControls) calculatorControls.style.display = 'block'; // --- Attach ONE event listener to the tbody for ALL button clicks --- tableTbodyElement.addEventListener('click', (event) => { const clickedButton = event.target.closest('button'); const buttonsContainer = clickedButton ? clickedButton.closest('.item-status-buttons') : null; if (clickedButton && buttonsContainer) { const itemName = buttonsContainer.dataset.itemName; const newState = parseInt(clickedButton.dataset.value, 10); const itemObj = allItems.find(i => i.name === itemName); if (itemObj) { itemObj.state = newState; } // Update active classes for buttons within this specific item's container Array.from(buttonsContainer.children).forEach(btn => { if (parseInt(btn.dataset.value, 10) === newState) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); generateOutputList(); // Auto-update the list } }); } /** * Generates the comma-separated output list. */ function generateOutputList() { const output = allItems .map(item => `${item.name}:${item.state}`) .join(','); outputListTextarea.value = output; } /** * Filters the item list based on search input by hiding/showing table rows. */ function filterItemList() { const searchTerm = itemSearchInput.value.toLowerCase(); allItems.forEach(item => { // We only care about the single primary row for each consolidated item for display const row = item.rowElement; if (row) { if (item.name.toLowerCase().includes(searchTerm)) { row.style.display = ''; // Show the primary row } else { row.style.display = 'none'; // Hide the primary row } } }); } /** * Copies the generated list to the clipboard. */ function copyToClipboard() { outputListTextarea.select(); outputListTextarea.setSelectionRange(0, 99999); try { document.execCommand('copy'); copyFeedback.textContent = 'Copied!'; setTimeout(() => { copyFeedback.textContent = '', 2000; }); } catch (err) { console.error('Failed to copy text: ', err); copyFeedback.textContent = 'Failed to copy.'; setTimeout(() => { copyFeedback.textContent = '', 2000; }); } } /** * Parses a list string and applies the settings to the item buttons. * @param {string} listString The comma-separated list of "ItemName:Value" */ function parseAndApplyList(listString) { const newSettings = new Map(); // Use a Map to store parsed settings and consolidate listString.split(',').forEach(entry => { const parts = entry.trim().split(':'); if (parts.length === 2) { const originalItemName = parts[0].trim(); const value = parseInt(parts[1].trim(), 10); if (!isNaN(value) && [ -1, 0, 1 ].includes(value)) { const consolidatedName = getConsolidatedItemName(originalItemName); const existingValue = newSettings.has(consolidatedName) ? newSettings.get(consolidatedName) : -2; // Use -2 as a placeholder for "not set" // Apply consolidation priority: Priority (1) > Default (0) > Hide (-1) if (value > existingValue) { // Higher value (more priority) wins newSettings.set(consolidatedName, value); } } } }); allItems.forEach(item => { const currentItemName = item.name; // This is already the consolidated name let newState = -1; // Default to Hide if not in the pasted list if (newSettings.has(currentItemName)) { newState = newSettings.get(currentItemName); } // Update the internal state item.state = newState; // Update the active class on the buttons for this item (only the primary one) if (item.buttonsCell) { // Ensure buttonsCell reference exists (this will be the primary one) const buttonsContainer = item.buttonsCell.querySelector('.item-status-buttons'); if (buttonsContainer) { Array.from(buttonsContainer.children).forEach(btn => { if (parseInt(btn.dataset.value, 10) === newState) { btn.classList.add('active'); } else { btn.classList.remove('active'); } }); } } }); // Re-generate the output list based on the new states generateOutputList(); } /** * Main entry point for the widget. */ function initWidget() { injectCalculatorControls(); // Inject the HTML for controls first // Now that controls are injected, attach event listeners to them if (itemSearchInput) { itemSearchInput.addEventListener('input', filterItemList); } if (copyButton) { copyButton.addEventListener('click', copyToClipboard); } // Add input listener to the output list textarea for reverse engineering if (outputListTextarea) { outputListTextarea.addEventListener('input', (event) => { parseAndApplyList(event.target.value); }); } // Use a MutationObserver to wait for the Cargo table's tbody to be generated const observer = new MutationObserver((mutations, obs) => { const table = document.getElementById('item-display-table'); // This is the ID of the table on the main page if (table && table.tBodies[0] && table.tBodies[0].rows.length > 0) { obs.disconnect(); // Stop observing once Cargo has populated the table initializeItemRows(); // Proceed with setting up buttons and interactivity } }); // Start observing the body for the main Cargo table to appear and be populated observer.observe(document.body, { childList: true, subtree: true }); // Fallback for immediate execution if DOM is already loaded and table is present if (document.readyState === 'complete') { const table = document.getElementById('item-display-table'); if (table && table.tBodies[0] && table.tBodies[0].rows.length > 0) { observer.disconnect(); initializeItemRows(); } } } // Run initialization when the DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initWidget); } else { initWidget(); } })();

More...
Item Name
Acorn 2
Alchemy Scroll 16
Amethyst Gem 400
Amethyst Necklace 600
Archer's Boots 10,000
Archer's Cape 20,000
Archer's Gloves 5,000
Armoury Key 2
Arrow Shafts 2
Aruba Root 10
Baked Potato 6
Bandit Mask 30
Barbarian Helm 1,000
Basic Rod 40
Bass 16
Beer 10
Black Bear Paw 300
Black Cape 40
Black Leather Boots 2,000
Black Leather Gloves 60
Blood Gloves 5,000
Blood Hat 900
Blood Hood 1,600
Blood Robe Bottoms 1,200
Blood Robe Top 1,500
Blood Scroll 40
Blue Cape 40
Blue Headband 200
Blue Santa Hat 200
Blue Wizard's Boots 6,000
Blue Wizard's Bottoms 750
Blue Wizard's Gloves 4,000
Blue Wizard's Hat 500
Blue Wizard's Top 1,000
Bluegill 20
Bone Meal 2
Bones 2
Bottle Of Rum 20
Bronze Arrowheads 6
Bronze Arrows 6
Bronze Bar 30
Bronze Battleaxe 200
Bronze Chainmail Body 200
Bronze Chestplate 400
Bronze Full Helm 120
Bronze Gloves 60
Bronze Great Sword 300
Bronze Hatchet 60
Bronze Helm 60
Bronze Longsword 120
Bronze Pickaxe 60
Bronze Platelegs 200
Bronze Scimitar 108
Bronze Shield 200
Brown Bear Paw 200
Burnt Food 2
Calcium Brew 40
Carbonado Gem 3,200
Carbonado Necklace 4,800
Carp 30
Carrot 10
Cave Bear Paw 400
Celadium Bar 10,000
Celadium Ore 2,000
Celadon Arrowheads 250
Celadon Arrows 300
Celadon Battleaxe 70,000
Celadon Chainmail Body 70,000
Celadon Chainmail Body (Gold Plating) 70,000
Celadon Chainmail Body (Silver Plating) 70,000
Celadon Chestplate 140,000
Celadon Chestplate (Gold Plating) 140,000
Celadon Chestplate (Silver Plating) 140,000
Celadon Full Helm 40,000
Celadon Full Helm (Gold Plating And Gold Plume) 40,000
Celadon Full Helm (Gold Plating) 40,000
Celadon Full Helm (Gold Plume) 40,000
Celadon Full Helm (Purple Plume) 40,000
Celadon Full Helm (Silver Plating And Gold Plume) 40,000
Celadon Full Helm (Silver Plating) 40,000
Celadon Gloves 20,000
Celadon Gloves (Gold Plating) 20,000
Celadon Gloves (Silver Plating) 20,000
Celadon Great Sword 105,000
Celadon Hatchet 20,000
Celadon Helm 20,000
Celadon Helm (Gold Plating) 20,000
Celadon Helm (Silver Plating) 20,000
Celadon Longsword 40,000
Celadon Orb (Fairly Bright) 2
Celadon Orb (Extremely Bright) 2
Celadon Orb (Dull) 2
Celadon Orb (Barely Glowing) 2
Celadon Orb (Faint Glow) 2
Celadon Orb (Dim Glow) 2
Celadon Pickaxe 20,000
Celadon Platelegs 70,000
Celadon Platelegs (Gold Plating) 70,000
Celadon Platelegs (Silver Plating) 70,000
Celadon Scimitar 36,000