diff --git a/core/code/artifact.js b/core/code/artifact.js index 12fa98bce..576d0d063 100644 --- a/core/code/artifact.js +++ b/core/code/artifact.js @@ -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 @@ -35,14 +37,12 @@ window.artifact.setup = function() { artifact._layer = new L.LayerGroup(); window.layerChooser.addOverlay(artifact._layer, 'Artifacts'); - $('') - .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, + }); } /** diff --git a/core/code/ornaments.js b/core/code/ornaments.js index 2ce29e64b..ebab0925b 100644 --- a/core/code/ornaments.js +++ b/core/code/ornaments.js @@ -1,4 +1,4 @@ -/* global L, dialog, log */ +/* global L, dialog, log, IITC */ /** * @namespace window.ornaments @@ -97,13 +97,13 @@ window.ornaments = { window.layerChooser.addOverlay(this.layers['Ornaments'], 'Ornaments'); window.layerChooser.addOverlay(this.layers['Excluded ornaments'], 'Excluded ornaments', {default: false}); - $('', { - 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, + }); }, /** diff --git a/core/code/region_scoreboard.js b/core/code/region_scoreboard.js index 28241422b..81768b97b 100644 --- a/core/code/region_scoreboard.js +++ b/core/code/region_scoreboard.js @@ -1,3 +1,5 @@ +/* global IITC -- eslint */ + /** * @file This file contains the code for displaying and handling the regional scoreboard. * @module region_scoreboard @@ -478,14 +480,12 @@ window.RegionScoreboardSetup = (function() { } }); } else { - $('') - .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, + }); } } }()); diff --git a/core/code/sidebar.js b/core/code/sidebar.js index afa96639c..2192ec10b 100644 --- a/core/code/sidebar.js +++ b/core/code/sidebar.js @@ -1,3 +1,5 @@ +/* global IITC -- eslint */ + /** * @file This file provides functions for working with the sidebar. * @module sidebar @@ -181,24 +183,20 @@ function setPermaLink () { * @function setupAddons */ function setupAddons () { - $('') - .html('Permalink') - .attr({ - id: 'permalink', - title: 'URL link to this map view' - }) - .on({ - mouseover: setPermaLink, - click: setPermaLink - }) - .appendTo('#toolbox'); - - $('') - .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(); diff --git a/core/code/toolbox.js b/core/code/toolbox.js new file mode 100644 index 000000000..b6daf5a8b --- /dev/null +++ b/core/code/toolbox.js @@ -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 = ``; + 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(); diff --git a/core/smartphone.css b/core/smartphone.css index a98f996a0..f480fef89 100644 --- a/core/smartphone.css +++ b/core/smartphone.css @@ -238,7 +238,7 @@ body { border: 2px outset #20A8B1; } -#toolbox > a { +#toolbox > a, #toolbox_component > a { padding: 5px; margin-top: 3px; margin-bottom: 3px; diff --git a/core/style.css b/core/style.css index 6759ad88e..3fb0b8879 100644 --- a/core/style.css +++ b/core/style.css @@ -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; @@ -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; diff --git a/core/total-conversion-build.js b/core/total-conversion-build.js index 631bd245c..e043661ad 100644 --- a/core/total-conversion-build.js +++ b/core/total-conversion-build.js @@ -119,42 +119,45 @@ document.head.innerHTML = '' // remove body element entirely to remove event listeners document.body = document.createElement('body'); -document.body.innerHTML = '' - + '
Loading, please wait
' - + '
' - + '' - + '' - + '' - + '
' // enable scrolling for small screens - + ' ' - + '
' - + '
' +document.body.innerHTML = + '
Loading, please wait
' + + '' + + '' + + '' + + '' + + '
' + // enable scrolling for small screens + ' ' + + '
' + + '
' + // avoid error by stock JS - + '
' - + ''; + '
' + + ''; /* ****************************************************************************************************************** */ diff --git a/plugins/bookmarks.js b/plugins/bookmarks.js index 3b246bcdc..cd1c52261 100644 --- a/plugins/bookmarks.js +++ b/plugins/bookmarks.js @@ -4,7 +4,7 @@ // @version 0.4.3 // @description Save your favorite Maps and Portals and move the intel map with a click. Works with sync. Supports Multi-Project-Extension -/* global L -- eslint */ +/* global L, IITC -- eslint */ /* exported setup, changelog --eslint */ var changelog = [ @@ -1225,8 +1225,6 @@ window.plugin.bookmarks.loadStorageBox = function() { plugin.bookmarks.htmlDisabledMessage = '
Plugin Bookmarks disabled*.
'; plugin.bookmarks.htmlStar = ''; - plugin.bookmarks.htmlCalldrawBox = 'Auto draw'; - plugin.bookmarks.htmlCallSetBox = 'Bookmarks Opt'; plugin.bookmarks.htmlMoveBtn = 'Show/Hide "Move" button' var actions = ''; @@ -1323,7 +1321,16 @@ window.plugin.bookmarks.initMPE = function(){ app.addPane("plugin-bookmarks", "Bookmarks", "ic_action_star"); window.addHook('paneChanged', window.plugin.bookmarks.onPaneChanged); } - $('#toolbox').append(window.plugin.bookmarks.htmlCallSetBox+window.plugin.bookmarks.htmlCalldrawBox); + IITC.toolbox.addButton({ + label: 'Bookmarks Opt', + action: window.plugin.bookmarks.manualOpt, + }); + IITC.toolbox.addButton({ + label: 'Auto draw', + title: 'Draw lines/triangles between bookmarked portals [q]', + action: window.plugin.bookmarks.dialogDrawer, + accesskey: 'q', + }); if(window.plugin.bookmarks.isSmart) { // $('#bookmarksBox.mobile #topBar').prepend(window.plugin.bookmarks.htmlCallSetBox+window.plugin.bookmarks.htmlCalldrawBox); // wonk in progress diff --git a/plugins/deleted/update-check.js b/plugins/deleted/update-check.js index 8d2eb4fa9..8fef0e48a 100644 --- a/plugins/deleted/update-check.js +++ b/plugins/deleted/update-check.js @@ -4,6 +4,7 @@ // @version 0.1.1 // @description **WORK IN PROGRESS** Check for updates for IITC and plugins against http://iitc.jonatkins.com/. Can also report status messages for known IITC issues. +/* global IITC -- eslint */ // use own namespace for plugin window.plugin.updateCheck = function() {}; @@ -290,8 +291,12 @@ window.plugin.updateCheck.open = function() { -window.plugin.updateCheck.setup = function() { - $('#toolbox').append(' Update check'); +window.plugin.updateCheck.setup = function () { + IITC.toolbox.addButton({ + label: 'Update check', + title: 'Check for IITC updates', + action: window.plugin.updateCheck.open, + }); }; var setup = window.plugin.updateCheck.setup; diff --git a/plugins/draw-tools.js b/plugins/draw-tools.js index ed43f820d..b693b11a9 100644 --- a/plugins/draw-tools.js +++ b/plugins/draw-tools.js @@ -4,6 +4,7 @@ // @version 0.10.1 // @description Allow drawing things onto the current map so you may plan your next move. Supports Multi-Project-Extension. +/* global IITC -- eslint */ /* exported setup, changelog --eslint */ var changelog = [ @@ -732,7 +733,12 @@ window.plugin.drawTools.boot = function() { }); //add options menu - $('#toolbox').append('DrawTools Opt'); + IITC.toolbox.addButton({ + label: 'DrawTools Opt', + title: '[x]', + action: window.plugin.drawTools.manualOpt, + accesskey: 'x', + }); $('head').append('