Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

API for toolbox buttons #692

Merged
merged 6 commits into from
Feb 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions core/code/artifact.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global IITC -- eslint */

/**
* @file Provides functions related to Ingress artifacts, including setup, data request, and processing functions.
* Added as part of the ingress #13magnus in november 2013, artifacts
Expand Down Expand Up @@ -35,14 +37,12 @@ window.artifact.setup = function() {
artifact._layer = new L.LayerGroup();
window.layerChooser.addOverlay(artifact._layer, 'Artifacts');

$('<a>')
.html('Artifacts')
.attr({
id: 'artifacts-toolbox-link',
title: 'Show artifact portal list'
})
.click(window.artifact.showArtifactList)
.appendTo('#toolbox');
IITC.toolbox.addButton({
id: 'artifacts-toolbox-link',
label: 'Artifacts',
title: 'Show artifact portal list',
action: window.artifact.showArtifactList,
});
}

/**
Expand Down
10 changes: 5 additions & 5 deletions core/code/ornaments.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global L, dialog, log */
/* global L, dialog, log, IITC */

/**
* @namespace window.ornaments
Expand Down Expand Up @@ -97,13 +97,13 @@ window.ornaments = {
window.layerChooser.addOverlay(this.layers['Ornaments'], 'Ornaments');
window.layerChooser.addOverlay(this.layers['Excluded ornaments'], 'Excluded ornaments', {default: false});

$('<a>', {
text:'Ornaments Opt',
IITC.toolbox.addButton({
id: 'ornaments-toolbox-link',
label: 'Ornaments Opt',
title: 'Edit ornament exclusions',
accesskey: 'o',
click: window.ornaments.ornamentsOpt})
.appendTo('#toolbox');
action: window.ornaments.ornamentsOpt,
});
},

/**
Expand Down
16 changes: 8 additions & 8 deletions core/code/region_scoreboard.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global IITC -- eslint */

/**
* @file This file contains the code for displaying and handling the regional scoreboard.
* @module region_scoreboard
Expand Down Expand Up @@ -478,14 +480,12 @@ window.RegionScoreboardSetup = (function() {
}
});
} else {
$('<a>')
.html('Region scores')
.attr({
id: 'scoreboard',
title: 'View regional scoreboard'
})
.click(showDialog)
.appendTo('#toolbox');
IITC.toolbox.addButton({
id: 'scoreboard',
label: 'Region scores',
title: 'View regional scoreboard',
action: showDialog,
});
}
}
}());
Expand Down
34 changes: 16 additions & 18 deletions core/code/sidebar.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/* global IITC -- eslint */

/**
* @file This file provides functions for working with the sidebar.
* @module sidebar
Expand Down Expand Up @@ -181,24 +183,20 @@ function setPermaLink () {
* @function setupAddons
*/
function setupAddons () {
$('<a>')
.html('Permalink')
.attr({
id: 'permalink',
title: 'URL link to this map view'
})
.on({
mouseover: setPermaLink,
click: setPermaLink
})
.appendTo('#toolbox');

$('<a>')
.html('About IITC')
.attr('id', 'about-iitc')
.css('cursor', 'help')
.click(aboutIITC)
.appendTo('#toolbox');
IITC.toolbox.addButton({
id: 'permalink',
label: 'Permalink',
title: 'URL link to this map view',
action: setPermaLink,
mouseover: setPermaLink,
});

IITC.toolbox.addButton({
id: 'about-iitc',
label: 'About IITC',
action: window.aboutIITC,
class: 'cursor_help',
});

window.artifact.setup();

Expand Down
239 changes: 239 additions & 0 deletions core/code/toolbox.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,239 @@
/* global IITC */

/**
* Toolbox API
*
* @memberof IITC
* @namespace toolbox
*/

/**
* @typedef {Object} ButtonArgs
* @property {string} [id] - Optional. The ID of the button.
* @property {string|undefined} label - The label text of the button.
* @property {Function|undefined} action - The onclick action for the button.
* @property {string|null} [class] - Optional. The class(es) for the button.
* @property {string|null} [title] - Optional. The title (tooltip) for the button.
* @property {string|null} [access_key] - Optional. The access key for the button.
* @property {Function|null} [mouseover] - Optional. The mouseover event for the button.
* @property {string|null} [icon] - Optional. Icon name from FontAwesome for the button.
*/

IITC.toolbox = {
buttons: {},
_defaultSortMethod: (a, b) => a.label.localeCompare(b.label),
sortMethod: (...args) => IITC.toolbox._defaultSortMethod(...args),

/**
* Adds a button to the toolbox.
*
* @param {ButtonArgs} buttonArgs - The arguments for the button.
* @returns {string|null} The ID of the added button or null if required parameters are missing.
*
* @example
* const buttonId = IITC.toolbox.addButton({
* label: 'AboutIITC',
* action: window.AboutIITC
* });
*
* @example
* const buttonId = IITC.toolbox.addButton({
* label: 'Test Button',
* action: () => alert('Clicked!')
* });
*/
addButton(buttonArgs) {
if (!buttonArgs.label) {
console.warn('Required parameter "label" are missing.');
return null;
}

if (!buttonArgs.action) {
console.warn('Required parameter "action" are missing.');
return null;
}

let id = buttonArgs.id || `toolbox-btn-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
this.buttons[id] = buttonArgs;

this._renderButton(id);
this._applySort();

return id;
},

/**
* Updates an existing button in the toolbox.
*
* @param {string} buttonId - The ID of the button to update.
* @param {ButtonArgs} newButtonArgs - The new arguments for the button.
* @returns {boolean} True if the button is successfully updated, false otherwise.
*
* @example
* const isUpdated = IITC.toolbox.updateButton(buttonId, { label: 'Updated Button', action: () => console.log('New Action') });
*/
updateButton(buttonId, newButtonArgs) {
if (this.buttons[buttonId]) {
Object.assign(this.buttons[buttonId], newButtonArgs);
this._renderButton(buttonId);
this._applySort();
return true;
} else {
console.warn(`Button with ID ${buttonId} not found.`);
return false;
}
},

/**
* Removes a button from the toolbox.
*
* @param {string} buttonId - The ID of the button to remove.
* @returns {boolean} True if the button is successfully removed, false otherwise.
*
* @example
* const isRemoved = IITC.toolbox.removeButton(buttonId);
*/
removeButton(buttonId) {
if (this.buttons[buttonId]) {
delete this.buttons[buttonId];
const buttonElement = document.getElementById(buttonId);
if (buttonElement) {
buttonElement.remove();
}
this._applySort();
return true;
} else {
console.warn(`Button with ID ${buttonId} not found for removal.`);
return false;
}
},

/**
* Internal method to render a button.
*
* @private
* @param {string} buttonId - The ID of the button to render.
*/
_renderButton(buttonId) {
const buttonData = this.buttons[buttonId];
if (!buttonData) return; // The button with the given ID was not found

let buttonElement = document.getElementById(buttonId) || document.createElement('a');
buttonElement.id = buttonId;
buttonElement.textContent = buttonData.label;
buttonElement.onclick = buttonData.action;

if (typeof buttonData.title === 'string') buttonElement.title = buttonData.title;
if (typeof buttonData.class === 'string') buttonElement.className = buttonData.class;
if (typeof buttonData.access_key === 'string') buttonElement.accessKey = buttonData.access_key;
if (typeof buttonData.mouseover === 'string') buttonElement.mouseover = buttonData.mouseover;

if (typeof buttonData.icon === 'string') {
const iconHTML = `<i class="fa ${buttonData.icon}"></i>`;
buttonElement.innerHTML = iconHTML + buttonElement.innerHTML;
}

const toolbox_component = document.querySelector('#toolbox_component');
if (!document.getElementById(buttonId)) {
toolbox_component.appendChild(buttonElement);
}
},

/**
* Internal method to apply sorting to the buttons.
*
* @private
*/
_applySort() {
const toolbox_component = document.querySelector('#toolbox_component');
const buttonElements = Array.from(toolbox_component.children);

try {
buttonElements.sort((a, b) => this.sortMethod(this.buttons[a.id], this.buttons[b.id]));
} catch (e) {
console.error('Sorting function produced error', e);
buttonElements.sort((a, b) => this._defaultSortMethod(this.buttons[a.id], this.buttons[b.id]));
}
buttonElements.forEach((buttonElement) => toolbox_component.appendChild(buttonElement));
},

/**
* Sets the sorting method for the toolbox buttons.
*
* @param {Function} sortMethod - The sorting method to be used.
* @returns {void}
*
* @example
* IITC.toolbox.setSortMethod((a, b) => a.label.localeCompare(b.label));
*/
setSortMethod(sortMethod) {
this.sortMethod = sortMethod;
this._applySort();
},

/**
* Internal method to synchronize the toolbox with the legacy toolbox.
*
* @private
* @returns {void}
*/
_syncWithLegacyToolbox() {
// Select the old toolbox element
const oldToolbox = document.querySelector('#toolbox');

// Function to process an individual button
const processButton = (node) => {
// Check if the node is an 'A' tag (anchor/link, which represents a button)
if (node.tagName === 'A') {
let iconClass = null;
// Find an icon element within the button, if it exists
const iconElement = node.querySelector('i.fa');
if (iconElement) {
// Extract the icon class
const iconClasses = Array.from(iconElement.classList).filter((cls) => cls.startsWith('fa-'));
if (iconClasses.length > 0) iconClass = iconClasses[0];
}

// Prepare the button arguments for either updating or adding the button
const buttonArgs = {
id: node.id,
label: node.textContent.trim(),
action: () => node.click(),
class: node.className,
title: node.title,
access_key: node.accessKey,
mouseover: node.mouseover,
icon: iconClass,
};

// Update an existing button or add a new one
buttonArgs['id'] = `legacy-toolbox-btn-${buttonArgs.id || buttonArgs.label}`;
if (this.buttons[buttonArgs.id]) {
this.updateButton(buttonArgs.id, buttonArgs);
} else {
this.addButton(buttonArgs);
}
}
};

// Initialize for existing buttons in the toolbox
oldToolbox.querySelectorAll('a').forEach(processButton);

// Mutation observer to watch for changes in the toolbox
const observer = new MutationObserver((mutations) => {
// Iterate through mutations
mutations.forEach((mutation) => {
// Process each added node and attribute changes
mutation.addedNodes.forEach(processButton);
if (mutation.type === 'attributes') {
processButton(mutation.target);
}
});
});

// Start observing the toolbox for changes
observer.observe(oldToolbox, { childList: true, subtree: true, attributes: true });
},
};

IITC.toolbox._syncWithLegacyToolbox();
2 changes: 1 addition & 1 deletion core/smartphone.css
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ body {
border: 2px outset #20A8B1;
}

#toolbox > a {
#toolbox > a, #toolbox_component > a {
padding: 5px;
margin-top: 3px;
margin-bottom: 3px;
Expand Down
10 changes: 9 additions & 1 deletion core/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -840,10 +840,14 @@ h3.title {
}

#toolbox {
display: none;
}

#toolbox, #toolbox_component {
text-align: left; /* centre didn't look as nice here as it did above in .linkdetails */
}

#toolbox > a {
#toolbox > a, #toolbox_component > a {
margin-left: 5px;
margin-right: 5px;
white-space: nowrap;
Expand Down Expand Up @@ -1276,6 +1280,10 @@ table.artifact .portal {
text-align: center;
}

.cursor_help {
cursor: help;
}

/* region scores */
.cellscore .ui-accordion-header, .cellscore .ui-accordion-content {
border: 1px solid #20a8b1;
Expand Down
Loading
Loading