major fixes for inventory

This commit is contained in:
erik 2025-07-02 10:29:36 +00:00
parent 00ef1d1f4b
commit 4d19e29847
10 changed files with 969 additions and 203 deletions

View file

@ -88,6 +88,13 @@
</a>
</div>
<!-- Player Dashboard link -->
<div class="player-dashboard-link">
<a href="#" id="playerDashboardBtn" onclick="openPlayerDashboard()">
👥 Player Dashboard
</a>
</div>
<!-- Container for sort and filter controls -->
<div id="sortButtons" class="sort-buttons"></div>

View file

@ -170,6 +170,14 @@
color: #000;
}
.subsection-label {
font-weight: bold;
margin-bottom: 2px;
display: block;
font-size: 9px;
color: #333;
}
.search-actions {
display: flex;
gap: 5px;
@ -325,6 +333,79 @@
.text-right {
text-align: right;
}
/* Column width control */
.narrow-col {
width: 120px;
max-width: 120px;
font-size: 9px;
line-height: 1.1;
}
.medium-col {
width: 150px;
max-width: 150px;
}
.set-col {
width: 100px;
max-width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.spells-cell {
font-size: 9px;
line-height: 1.2;
}
.legendary-cantrip {
color: #ffd700;
font-weight: bold;
}
.regular-spell {
color: #88ccff;
}
/* Pagination controls */
.pagination-controls {
padding: 12px 16px;
text-align: center;
background: #f8f8f8;
border-top: 1px solid #ddd;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
font-size: 11px;
}
.pagination-controls button {
padding: 4px 8px;
border: 1px solid #999;
background: #e0e0e0;
font-size: 10px;
cursor: pointer;
border-radius: 3px;
}
.pagination-controls button:hover:not(:disabled) {
background: #d0d0d0;
}
.pagination-controls button:disabled {
background: #f0f0f0;
color: #999;
cursor: not-allowed;
}
.pagination-info {
font-weight: bold;
color: #333;
margin: 0 15px;
}
</style>
</head>
<body>
@ -442,6 +523,18 @@
<input type="number" id="searchMaxHealBoost" placeholder="Max">
</div>
</div>
<!-- New Rating Filters -->
<div class="filter-row">
<div class="filter-group">
<label>Vitality:</label>
<input type="number" id="searchMinVitalityRating" placeholder="Min">
</div>
<div class="filter-group">
<label>Dmg Resist:</label>
<input type="number" id="searchMinDamageResistRating" placeholder="Min">
</div>
</div>
<!-- Equipment Sets -->
<div class="filter-section">
@ -545,54 +638,148 @@
</div>
<!-- Legendary Weapon Skills -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_finesse" value="Legendary Finesse Weapons">
<input type="checkbox" id="cantrip_legendary_finesse" value="Legendary Finesse Weapon Aptitude">
<label for="cantrip_legendary_finesse">Finesse</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_heavy" value="Legendary Heavy Weapons">
<input type="checkbox" id="cantrip_legendary_heavy" value="Legendary Heavy Weapon Aptitude">
<label for="cantrip_legendary_heavy">Heavy</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_light" value="Legendary Light Weapons">
<input type="checkbox" id="cantrip_legendary_light" value="Legendary Light Weapon Aptitude">
<label for="cantrip_legendary_light">Light</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_missile" value="Legendary Missile Weapons">
<input type="checkbox" id="cantrip_legendary_missile" value="Legendary Missile Weapon Aptitude">
<label for="cantrip_legendary_missile">Missile</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_twohanded" value="Legendary Two Handed Combat">
<input type="checkbox" id="cantrip_legendary_twohanded" value="Legendary Two Handed Combat Aptitude">
<label for="cantrip_legendary_twohanded">Two-handed</label>
</div>
<!-- Legendary Magic Skills -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_war" value="Legendary War Magic">
<input type="checkbox" id="cantrip_legendary_war" value="Legendary War Magic Aptitude">
<label for="cantrip_legendary_war">War Magic</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_void" value="Legendary Void Magic">
<input type="checkbox" id="cantrip_legendary_void" value="Legendary Void Magic Aptitude">
<label for="cantrip_legendary_void">Void Magic</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_creature" value="Legendary Creature Enchantment">
<input type="checkbox" id="cantrip_legendary_creature" value="Legendary Creature Enchantment Aptitude">
<label for="cantrip_legendary_creature">Creature</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_item" value="Legendary Item Enchantment">
<input type="checkbox" id="cantrip_legendary_item" value="Legendary Item Enchantment Aptitude">
<label for="cantrip_legendary_item">Item</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_life" value="Legendary Life Magic">
<input type="checkbox" id="cantrip_legendary_life" value="Legendary Life Magic Aptitude">
<label for="cantrip_legendary_life">Life Magic</label>
</div>
<!-- Legendary Defense -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_magic_defense" value="Legendary Magic Defense">
<input type="checkbox" id="cantrip_legendary_magic_defense" value="Legendary Magic Resistance">
<label for="cantrip_legendary_magic_defense">Magic Def</label>
</div>
<!-- Combat Skills -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_melee_defense" value="Legendary Melee Defense">
<label for="cantrip_legendary_melee_defense">Melee Def</label>
<input type="checkbox" id="cantrip_legendary_blood_thirst" value="Legendary Blood Thirst">
<label for="cantrip_legendary_blood_thirst">Blood Thirst</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_sneak_attack" value="Legendary Sneak Attack Prowess">
<label for="cantrip_legendary_sneak_attack">Sneak Attack</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_defender" value="Legendary Defender">
<label for="cantrip_legendary_defender">Defender</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_recklessness" value="Legendary Recklessness Prowess">
<label for="cantrip_legendary_recklessness">Recklessness</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_shield" value="Legendary Shield Aptitude">
<label for="cantrip_legendary_shield">Shield</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_dual_wield" value="Legendary Dual Wield Aptitude">
<label for="cantrip_legendary_dual_wield">Dual Wield</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_dirty_fighting" value="Legendary Dirty Fighting Prowess">
<label for="cantrip_legendary_dirty_fighting">Dirty Fighting</label>
</div>
<!-- Magic/Utility Skills -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_summoning" value="Legendary Summoning Prowess">
<label for="cantrip_legendary_summoning">Summoning</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_healing" value="Legendary Healing Prowess">
<label for="cantrip_legendary_healing">Healing</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_arcane" value="Legendary Arcane Prowess">
<label for="cantrip_legendary_arcane">Arcane</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_tinkering" value="Legendary Magic Item Tinkering Expertise">
<label for="cantrip_legendary_tinkering">Tinkering</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_mana_conversion" value="Legendary Mana Conversion Prowess">
<label for="cantrip_legendary_mana_conversion">Mana Convert</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_leadership" value="Legendary Leadership">
<label for="cantrip_legendary_leadership">Leadership</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_deception" value="Legendary Deception Prowess">
<label for="cantrip_legendary_deception">Deception</label>
</div>
<!-- Defensive Spells -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_impenetrability" value="Legendary Impenetrability">
<label for="cantrip_legendary_impenetrability">Impenetrability</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_impregnability" value="Legendary Impregnability">
<label for="cantrip_legendary_impregnability">Impregnability</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_invulnerability" value="Legendary Invulnerability">
<label for="cantrip_legendary_invulnerability">Invulnerability</label>
</div>
<!-- Specialized/Rare -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_hermetic_link" value="Legendary Hermetic Link">
<label for="cantrip_legendary_hermetic_link">Hermetic Link</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_person_attunement" value="Legendary Person Attunement">
<label for="cantrip_legendary_person_attunement">Person Attune</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_spirit_thirst" value="Legendary Spirit Thirst">
<label for="cantrip_legendary_spirit_thirst">Spirit Thirst</label>
</div>
<!-- Bane Spells -->
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_piercing_bane" value="Legendary Piercing Bane">
<label for="cantrip_legendary_piercing_bane">Piercing Bane</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="cantrip_legendary_storm_bane" value="Legendary Storm Bane">
<label for="cantrip_legendary_storm_bane">Storm Bane</label>
</div>
</div>
</div>
@ -636,6 +823,77 @@
</div>
</div>
<!-- Equipment Slots -->
<div class="filter-section">
<label class="section-label">Equipment Slots:</label>
<!-- Armor Slots -->
<div class="checkbox-container" id="armor-slots">
<label class="subsection-label">Armor:</label>
<div class="checkbox-item">
<input type="checkbox" id="slot_head" value="Head">
<label for="slot_head">Head</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_chest" value="Chest">
<label for="slot_chest">Chest</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_abdomen" value="Abdomen">
<label for="slot_abdomen">Abdomen</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_upper_arms" value="Upper Arms">
<label for="slot_upper_arms">Upper Arms</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_lower_arms" value="Lower Arms">
<label for="slot_lower_arms">Lower Arms</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_hands" value="Hands">
<label for="slot_hands">Hands</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_upper_legs" value="Upper Legs">
<label for="slot_upper_legs">Upper Legs</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_lower_legs" value="Lower Legs">
<label for="slot_lower_legs">Lower Legs</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_feet" value="Feet">
<label for="slot_feet">Feet</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_shield" value="Shield">
<label for="slot_shield">Shield</label>
</div>
</div>
<!-- Jewelry Slots -->
<div class="checkbox-container" id="jewelry-slots">
<label class="subsection-label">Jewelry:</label>
<div class="checkbox-item">
<input type="checkbox" id="slot_neck" value="Neck">
<label for="slot_neck">Neck</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_ring" value="Ring">
<label for="slot_ring">Ring</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_bracelet" value="Bracelet">
<label for="slot_bracelet">Bracelet</label>
</div>
<div class="checkbox-item">
<input type="checkbox" id="slot_trinket" value="Trinket">
<label for="slot_trinket">Trinket</label>
</div>
</div>
</div>
<div class="search-actions">
<button type="button" class="btn btn-secondary" id="clearBtn">Clear All</button>
<button type="submit" class="btn btn-primary">Search Items</button>

View file

@ -20,6 +20,11 @@ let currentSort = {
// Store current search results for client-side sorting
let currentResultsData = null;
// Pagination state
let currentPage = 1;
let itemsPerPage = 5000; // 5k items per page for good performance
let totalPages = 1;
// Initialize the application
document.addEventListener('DOMContentLoaded', function() {
// Get DOM elements after DOM is loaded
@ -39,7 +44,7 @@ function initializeEventListeners() {
// Form submission
searchForm.addEventListener('submit', async (e) => {
e.preventDefault();
await performSearch();
await performSearch(true); // Reset to page 1 on new search
});
// Clear button
@ -167,6 +172,10 @@ function clearAllFields() {
// Reset slot filter
document.getElementById('slotFilter').value = '';
// Reset pagination
currentPage = 1;
totalPages = 1;
// Reset results and clear stored data
currentResultsData = null;
searchResults.innerHTML = '<div class="no-results">Enter search criteria above and click "Search Items" to find inventory items.</div>';
@ -196,7 +205,12 @@ function handleSlotFilterChange() {
/**
* Perform the search based on form inputs
*/
async function performSearch() {
async function performSearch(resetPage = false) {
// Reset to page 1 if this is a new search (not pagination)
if (resetPage) {
currentPage = 1;
}
searchResults.innerHTML = '<div class="loading">🔍 Searching inventory...</div>';
try {
@ -214,11 +228,13 @@ async function performSearch() {
// Store results for client-side re-sorting
currentResultsData = data;
// Update pagination state
updatePaginationState(data);
// Apply client-side slot filtering
applySlotFilter(data);
// Sort the results client-side before displaying
sortResults(data);
// Display results (already sorted by server)
displayResults(data);
} catch (error) {
@ -280,6 +296,8 @@ function buildSearchParameters() {
addParam(params, 'max_damage_rating', 'searchMaxDamageRating');
addParam(params, 'min_heal_boost_rating', 'searchMinHealBoost');
addParam(params, 'max_heal_boost_rating', 'searchMaxHealBoost');
addParam(params, 'min_vitality_rating', 'searchMinVitalityRating');
addParam(params, 'min_damage_resist_rating', 'searchMinDamageResistRating');
// Requirements parameters
addParam(params, 'min_level', 'searchMinLevel');
@ -308,8 +326,19 @@ function buildSearchParameters() {
params.append('legendary_cantrips', allSpells.join(','));
}
// Pagination only - sorting will be done client-side
params.append('limit', '1000'); // Show all items on one page
// Equipment slot filters
const selectedSlots = getSelectedSlots();
if (selectedSlots.length > 0) {
params.append('slot_names', selectedSlots.join(','));
}
// Pagination parameters
params.append('page', currentPage);
params.append('limit', itemsPerPage);
// Sorting parameters
params.append('sort_by', currentSort.field);
params.append('sort_dir', currentSort.direction);
return params;
}
@ -357,6 +386,22 @@ function getSelectedProtections() {
return selectedProtections;
}
/**
* Get selected equipment slots from checkboxes
*/
function getSelectedSlots() {
const selectedSlots = [];
// Get armor slots
document.querySelectorAll('#armor-slots input[type="checkbox"]:checked').forEach(cb => {
selectedSlots.push(cb.value);
});
// Get jewelry slots
document.querySelectorAll('#jewelry-slots input[type="checkbox"]:checked').forEach(cb => {
selectedSlots.push(cb.value);
});
return selectedSlots;
}
/**
* Display search results in the UI
*/
@ -383,12 +428,16 @@ function displayResults(data) {
<th class="sortable" data-sort="character_name">Character${getSortIcon('character_name')}</th>
<th class="sortable" data-sort="name">Item Name${getSortIcon('name')}</th>
<th class="sortable" data-sort="item_type_name">Type${getSortIcon('item_type_name')}</th>
<th class="text-right sortable" data-sort="slot_name">Slot${getSortIcon('slot_name')}</th>
<th class="text-right sortable" data-sort="coverage">Coverage${getSortIcon('coverage')}</th>
<th class="text-right sortable" data-sort="armor_level">Armor${getSortIcon('armor_level')}</th>
<th class="sortable" data-sort="spell_names">Spells/Cantrips${getSortIcon('spell_names')}</th>
<th class="text-right narrow-col sortable" data-sort="slot_name">Slot${getSortIcon('slot_name')}</th>
<th class="text-right narrow-col sortable" data-sort="coverage">Coverage${getSortIcon('coverage')}</th>
<th class="text-right sortable" data-sort="armor">Armor${getSortIcon('armor')}</th>
<th class="set-col sortable" data-sort="item_set">Set${getSortIcon('item_set')}</th>
<th class="medium-col sortable" data-sort="spell_names">Spells/Cantrips${getSortIcon('spell_names')}</th>
<th class="text-right sortable" data-sort="crit_damage_rating">Crit Dmg${getSortIcon('crit_damage_rating')}</th>
<th class="text-right sortable" data-sort="damage_rating">Dmg Rating${getSortIcon('damage_rating')}</th>
<th class="text-right sortable" data-sort="heal_boost_rating">Heal Boost${getSortIcon('heal_boost_rating')}</th>
<th class="text-right sortable" data-sort="vitality_rating">Vitality${getSortIcon('vitality_rating')}</th>
<th class="text-right sortable" data-sort="damage_resist_rating">Dmg Resist${getSortIcon('damage_resist_rating')}</th>
<th class="text-right sortable" data-sort="last_updated">Last Updated${getSortIcon('last_updated')}</th>
</tr>
</thead>
@ -399,14 +448,19 @@ function displayResults(data) {
const armor = item.armor_level > 0 ? item.armor_level : '-';
const critDmg = item.crit_damage_rating > 0 ? item.crit_damage_rating : '-';
const dmgRating = item.damage_rating > 0 ? item.damage_rating : '-';
const healBoostRating = item.heal_boost_rating > 0 ? item.heal_boost_rating : '-';
const vitalityRating = item.vitality_rating > 0 ? item.vitality_rating : '-';
const damageResistRating = item.damage_resist_rating > 0 ? item.damage_resist_rating : '-';
const status = item.is_equipped ? '⚔️ Equipped' : '📦 Inventory';
const statusClass = item.is_equipped ? 'status-equipped' : 'status-inventory';
// Use the slot_name provided by the API instead of incorrect mapping
const slot = item.slot_name || 'Unknown';
// Replace commas with line breaks for better display
const slot = item.slot_name ? item.slot_name.replace(/,\s*/g, '<br>') : 'Unknown';
// Coverage placeholder - will need to be added to backend later
const coverage = item.coverage || '-';
// Replace commas with line breaks for better display
const coverage = item.coverage ? item.coverage.replace(/,\s*/g, '<br>') : '-';
// Format last updated timestamp
const lastUpdated = item.last_updated ?
@ -444,17 +498,30 @@ function displayResults(data) {
// Get item type for display
const itemType = item.item_type_name || '-';
// Format equipment set name
let setDisplay = '-';
if (item.item_set) {
// Remove redundant "Set" prefix if present
setDisplay = item.item_set.replace(/^Set\s+/i, '');
// Also handle if it ends with " Set"
setDisplay = setDisplay.replace(/\s+Set$/i, '');
}
html += `
<tr>
<td>${item.character_name}</td>
<td class="item-name">${displayName}</td>
<td>${itemType}</td>
<td class="text-right">${slot}</td>
<td class="text-right">${coverage}</td>
<td class="text-right narrow-col">${slot}</td>
<td class="text-right narrow-col">${coverage}</td>
<td class="text-right">${armor}</td>
<td class="spells-cell">${spellsDisplay}</td>
<td class="set-col" title="${setDisplay}">${setDisplay}</td>
<td class="spells-cell medium-col">${spellsDisplay}</td>
<td class="text-right">${critDmg}</td>
<td class="text-right">${dmgRating}</td>
<td class="text-right">${healBoostRating}</td>
<td class="text-right">${vitalityRating}</td>
<td class="text-right">${damageResistRating}</td>
<td class="text-right">${lastUpdated}</td>
</tr>
`;
@ -465,11 +532,18 @@ function displayResults(data) {
</table>
`;
// Add pagination info if needed
if (data.total_pages > 1) {
// Add pagination controls if needed
if (totalPages > 1) {
const isFirstPage = currentPage === 1;
const isLastPage = currentPage === totalPages;
html += `
<div style="padding: 16px 24px; text-align: center; color: #5f6368; border-top: 1px solid #e8eaed;">
Showing page ${data.page} of ${data.total_pages} pages
<div class="pagination-controls">
<button onclick="goToPage(1)" ${isFirstPage ? 'disabled' : ''}>First</button>
<button onclick="previousPage()" ${isFirstPage ? 'disabled' : ''}> Previous</button>
<span class="pagination-info">Page ${currentPage} of ${totalPages} (${data.total_count} total items)</span>
<button onclick="nextPage()" ${isLastPage ? 'disabled' : ''}>Next </button>
<button onclick="goToPage(${totalPages})" ${isLastPage ? 'disabled' : ''}>Last</button>
</div>
`;
}
@ -566,20 +640,9 @@ function handleSort(field) {
currentSort.direction = 'asc';
}
// Re-display current results with new sorting (no new search needed)
if (currentResultsData) {
// Reset items to original unfiltered data
const originalData = JSON.parse(JSON.stringify(currentResultsData));
// Apply slot filtering first
applySlotFilter(originalData);
// Then apply sorting
sortResults(originalData);
// Display results
displayResults(originalData);
}
// Reset to page 1 and perform new search with updated sort
currentPage = 1;
performSearch();
}
/**
@ -722,3 +785,41 @@ function displaySetAnalysisResults(data) {
setAnalysisResults.innerHTML = html;
}
/**
* Update pagination state from API response
*/
function updatePaginationState(data) {
totalPages = data.total_pages || 1;
// Current page is already tracked in currentPage
}
/**
* Go to a specific page
*/
function goToPage(page) {
if (page < 1 || page > totalPages || page === currentPage) {
return;
}
currentPage = page;
performSearch();
}
/**
* Go to next page
*/
function nextPage() {
if (currentPage < totalPages) {
goToPage(currentPage + 1);
}
}
/**
* Go to previous page
*/
function previousPage() {
if (currentPage > 1) {
goToPage(currentPage - 1);
}
}

View file

@ -1989,4 +1989,12 @@ function openQuestStatus() {
window.open('/quest-status.html', '_blank');
}
/**
* Opens the Player Dashboard interface in a new browser tab.
*/
function openPlayerDashboard() {
// Open the Player Dashboard page in a new tab
window.open('/player-dashboard.html', '_blank');
}

View file

@ -1460,6 +1460,34 @@ body.noselect, body.noselect * {
margin: -2px -4px;
}
.player-dashboard-link {
margin: 0 0 12px;
padding: 8px 12px;
background: var(--card);
border: 1px solid #88f;
border-radius: 4px;
text-align: center;
}
.player-dashboard-link a {
color: #88f;
text-decoration: none;
font-size: 0.9rem;
font-weight: 500;
display: block;
cursor: pointer;
user-select: none;
transition: all 0.2s ease;
}
.player-dashboard-link a:hover {
color: #fff;
background: rgba(136, 136, 255, 0.1);
border-radius: 2px;
padding: 2px 4px;
margin: -2px -4px;
}
/* Sortable column styles for inventory tables */
.sortable {
cursor: pointer;