// ==UserScript==
// @name Net-7 Source Vault Separate Script
// @namespace http://tampermonkey.net/
// @version 0.2
// @description Handles the actual work of adding and changing elements on the page.
// @author You
// @match https://www.net-7.org/*
// @grant none
// @license CC BY-NC
// ==/UserScript==
const maxSelectedItems = 12;
let selectedItems = [];
function isItemParent(parent, potential) {
const parent_iid = parent.getAttribute('iid');
const parent_slot = parent.getAttribute('slot');
const potential_iid = potential.getAttribute('iid');
const potential_slot = potential.getAttribute('slot');
const is_clone = potential.getAttribute('data-clone');
return parent_iid === potential_iid && parent_slot === potential_slot && is_clone;
}
// Function to convert Arabic numeral to Roman numeral
function getRomanNumeral(num) {
const romanNumerals = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
return romanNumerals[num];
}
// Function to handle unchecking checkbox
function handleCheckboxUncheck(event) {
const uncheckedValue = event.target.value;
//TO-DO: implement graying out
}
// Function to handle checkbox changes
function handleCheckboxChange(event) {
const selectedValue = event.target.value;
//TO-DO: implement graying out
}
// Function to check if checkboxes already exist
function hasCheckboxes() {
return document.getElementById('checkboxesRow') !== null;
}
// Function to insert checkboxes for Roman numerals 1-9 ahead of the given element
function insertCheckboxes(targetElement) {
// Create a div element for the checkboxes row
const checkboxesRow = document.createElement('div');
checkboxesRow.id = 'checkboxesRow';
// Create and append checkboxes for Roman numerals 1-9
for (let i = 1; i <= 9; i++) {
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.value = i;
checkbox.id = 'checkbox' + i;
checkbox.addEventListener('change', handleCheckboxChange);
const label = document.createElement('label');
label.innerHTML = getRomanNumeral(i);
label.htmlFor = 'checkbox' + i;
checkboxesRow.appendChild(checkbox);
checkboxesRow.appendChild(label);
}
// Insert the checkboxes row ahead of the target element
if (targetElement.parentNode) {
targetElement.parentNode.insertBefore(checkboxesRow, targetElement);
}
}
function getRelevantHTMLValues(inner_html) {
// Extract inner HTML without styles and JS
const innerHTMLWithoutStyles = inner_html.replace(/<style\b[^>]*>[\s\S]*?<\/script>/g, '');
const parsedData = [];
//console.log("Stripped HTML is ", innerHTMLWithoutStyles);
// Create a temporary div to parse the HTML content
const tempDiv = document.createElement('div');
tempDiv.innerHTML = innerHTMLWithoutStyles;
// Extract and parse relevant information
const items = tempDiv.querySelectorAll('.vault_draggable');
items.forEach(item => {
const itemName = item.querySelector('.acc_vault_moreinfo').getAttribute('name');
const itemLevel = item.querySelector('.pad-level').textContent;
const itemStack = item.querySelector('.pad-stack').textContent;
const item_id = item.getAttribute('iid');
const slot_id = item.getAttribute('slot');
// Extract 'data-tooltip' attribute
const dataTooltip = item.getAttribute('data-tooltip');
// Parse 'data-tooltip' content so we can extract stuff from it to filter with.
const tooltipParser = new DOMParser();
const tooltipDoc = tooltipParser.parseFromString(dataTooltip, 'text/html');
// Extract the text content after 'Manufacturer'
const manufacturerField = Array.from(tooltipDoc.querySelectorAll('.nestedleft')).find(el => el.textContent.includes('Manufacturer'));
const manufacturerValue = (manufacturerField && manufacturerField.nextSibling) ? manufacturerField.nextSibling.textContent.trim() : 'non-pm';
// Extract img src for both pad-background and pad-item
const backgroundImgSrc = item.querySelector('.pad-background').src;
const itemImgSrc = item.querySelector('.pad-item').src;
// Do something with the parsed information
/*console.log('Item Name:', itemName);
console.log('Item Level:', itemLevel);
console.log('Item Stack:', itemStack);
console.log('Tooltip:', dataTooltip);
console.log('Item made by: ', manufacturerValue);*/
// Create an object with the extracted values
const parsedItem = {
itemName,
itemLevel,
itemStack,
item_id,
slot_id,
dataTooltip,
manufacturerValue,
backgroundImgSrc,
itemImgSrc
};
// Push the object into the array
parsedData.push(parsedItem);
});
// Return the array containing parsed data
return parsedData;
}
function toggleSelectedState(itemDiv) {
const isUiSelectable = itemDiv.classList.contains('ui-selectable');
const isUiStateDisabled = itemDiv.classList.contains('green_checkmark');
// Check if adding a new item would exceed the maximum limit
if (selectedItems.length + 1 > maxSelectedItems) {
return;
}
// Get all occupied table slots
const trasfer_slots_parent = document.querySelector('#parkslots');
const occupiedSlots = trasfer_slots_parent.querySelectorAll('table td[aria-disabled="true"]');
// Find the first available slot
const emptySlot = trasfer_slots_parent.querySelector('td[aria-disabled="false"], td:not([aria-disabled])');
console.log("Got ", emptySlot, "as empty slot");
if(isUiSelectable && emptySlot) {
//Make updates to the 12 slot array of items being moved
emptySlot.textContent = '';
const empty_slot_id = emptySlot.id;
emptySlot.setAttribute('aria-disabled','true');
//Mark the item as being in the move list before we clone it.
itemDiv.classList.remove('ui-selectable');
itemDiv.setAttribute('aria-disabled','true');
//Create a clone of the vault item and display it in the table of items to move.
const cloned_item = itemDiv.cloneNode(true);
cloned_item.setAttribute('data-clone', 'true');
//Some fixes are needed to get it to display correctly
cloned_item.classList.add('pad-container');
const clone_children = cloned_item.children;
for (let i = 0; i < clone_children.length; i++) {
const child = clone_children[i];
if (child.classList.contains('pad-background')) {
child.classList.remove('pad-background');
child.classList.add('custom_pad-background');
}
if (child.classList.contains('pad-item')) {
child.classList.remove('pad-item');
child.classList.add('custom_pad-item');
}
if (child.classList.contains('pad-level')) {
child.classList.remove('pad-level');
child.classList.add('custom_pad-level');
}
if (child.classList.contains('pad-stack')) {
child.classList.remove('pad-stack');
child.classList.add('custom_pad-stack');
}
}
//Finally, append the (fixed clone) to the table of stuff to move
emptySlot.appendChild(cloned_item);
// Add the item to the selectedItems array if not already selected
if (!selectedItems.includes(itemDiv)) {
selectedItems.push(itemDiv);
}
//Now update the item itself in the vault:
itemDiv.setAttribute('park_slot',empty_slot_id);
itemDiv.classList.add('ui-state-disabled');
//Give the item being moved a green tint so we can see which ones are moving.
const image_layer = itemDiv.querySelector('.pad-item');
image_layer.classList.add('mark_as_moving');
} else if(!isUiSelectable) {
occupiedSlots.forEach(slot => {
const cloned_item = slot.querySelector('div');
if (isItemParent(itemDiv, cloned_item)) {
slot.removeChild(cloned_item);
slot.textContent = "Slot# " + parseInt(slot.id.split('_')[2]);
slot.setAttribute('aria-disabled', 'false');
}
});
itemDiv.removeAttribute('park_slot');
itemDiv.removeAttribute('aria-disabled');
itemDiv.classList.add('ui-selectable');
itemDiv.classList.remove('green_checkmark', 'ui-state-disabled');
//Remove the green tint.
const image_layer = itemDiv.querySelector('.pad-item');
image_layer.classList.remove('mark_as_moving');
}
}
function handleItemClick(event) {
const closest_pad_container = event.target.closest('.pad-container');
console.log("Item clicked was", event.target);
if(event.ctrlKey) {
console.log("Ctrl-click detected on", event.target);
//Ctrl-click = Open link to item as if we did a DB search.
event.target.click();
} else {
// Check if the click originated from an element within pad-container
const isClickInsidePadContainer = closest_pad_container !== null;
if (isClickInsidePadContainer) {
const item_div = closest_pad_container.querySelector('.tooltip');
console.log("Toggling click state on ", item_div);
toggleSelectedState(item_div);
}
console.log("Not Toggling click state on ", event.target);
}
}
function createVaultCell(row_data, parent_element) {
const item_div_root = document.createElement('div');
item_div_root.classList.add("pad");
item_div_root.style.cssText = "float: left;";
//Add child to parent so event handler registration works.
parent_element.appendChild(item_div_root);
const pad_cont_div = document.createElement('div');
pad_cont_div.classList.add("pad-container");
pad_cont_div.addEventListener('click', handleItemClick);
item_div_root.appendChild(pad_cont_div);
const tooltip_div = document.createElement('div');
tooltip_div.classList.add("tooltip", "ui-selectable");
tooltip_div.setAttribute("iid", row_data.item_id);
tooltip_div.setAttribute("slot", row_data.slot_id);
//Will either be a player name or 'non-pm' for looted/vendor bought/etc items.
tooltip_div.setAttribute("mfg_by", row_data.manufacturerValue);
tooltip_div.setAttribute("ilvl", row_data.itemLevel);
tooltip_div.setAttribute("stack_size", row_data.itemStack);
//Do this last
tooltip_div.setAttribute("tooltip", row_data.dataTooltip)
pad_cont_div.appendChild(tooltip_div);
const background_img = document.createElement('img');
background_img.classList.add("pad-background");
background_img.src = row_data.backgroundImgSrc;
tooltip_div.appendChild(background_img);
const item_img = document.createElement('img');
item_img.classList.add("pad-item");
item_img.src = row_data.itemImgSrc;
tooltip_div.appendChild(item_img);
const span_level = document.createElement('span');
span_level.classList.add("pad-level");
span_level.textContent = row_data.itemLevel;
tooltip_div.appendChild(span_level);
const span_stack = document.createElement('span');
span_stack.classList.add("pad-stack");
span_stack.textContent = row_data.itemStack;
tooltip_div.appendChild(span_stack);
}
function createVaultPage(orig_extracted_data, parent_node) {
const source_vault_root = document.createElement('div');
source_vault_root.classList.add("clearfix", "inventory_pane");
source_vault_root.style.cssText = "font-size: 10px; width:84em;";
parent_node.appendChild(source_vault_root);
//Now go through each item in the vault and create its box.
orig_extracted_data.forEach(item => {
createVaultCell(item, source_vault_root);
})
}
// Define the function globally using unsafeWindow
window.handleHTMLModification = function(addedNode) {
//console.log('Handling HTML modification in myfunction:', addedNode.innerHTML);
// Insert checkboxes for Roman numerals 1-9 if they don't exist
if (!hasCheckboxes()) {
insertCheckboxes(addedNode);
}
const orig_extracted_data = getRelevantHTMLValues(addedNode.innerHTML);
//Wipe out the node's current HTML, we will rebuild it.
addedNode.innerHTML = '';
//addedNode is the mutation target, ie, source_vault, and is passed by reference.
createVaultPage(orig_extracted_data, addedNode);
};