diff --git a/.gitignore b/.gitignore index 40c3c4a..4d9710a 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,5 @@ -manifest.json \ No newline at end of file +*.log +*.orig +node_modules/* +jsdoc-out/* +fetch_scripts/node_modules/* \ No newline at end of file diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100644 index 0000000..60a9fd8 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,8 @@ +# .husky/pre-commit +#!/usr/bin/env sh +. "$(dirname "$0")/_/husky.sh" + +npm test +# npx lint-staged + +# exit 1 # enable this line to purposely fail the commit (testing) \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..ae66f13 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +README.md +package.json +.gitignore +src/libs/* \ No newline at end of file diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..eb0773b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,22 @@ +{ + "arrowParens": "avoid", + "bracketSpacing": true, + "htmlWhitespaceSensitivity": "css", + "insertPragma": false, + "bracketSameLine": false, + "printWidth": 80, + "proseWrap": "always", + "quoteProps": "as-needed", + "requirePragma": false, + "semi": false, + "singleQuote": true, + "tabWidth": 4, + "trailingComma": "es5", + "useTabs": false, + "overrides": [ + { + "files": ".prettierrc", + "options": { "parser": "json" } + } + ] +} diff --git a/.stylelint.config.mjs b/.stylelint.config.mjs new file mode 100644 index 0000000..6013766 --- /dev/null +++ b/.stylelint.config.mjs @@ -0,0 +1,6 @@ +/** @type {import('stylelint').Config} */ +export default { + rules: { + "color-no-invalid-hex": true + } +}; \ No newline at end of file diff --git a/README.md b/README.md index c610d54..809fe00 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Rogue Dex is a browser extension that connects to Pokerogue and uses PokeAPI to ## Installation - Clone this repository to your local machine. - Load the extension in developer mode in your browser. + - `Load unpacked` -> select `src` folder of this repository if using development code, or `dist` if using distribution code (if available). - Launch Pokerogue and start playing to see Pokemon data in action. ## Usage @@ -17,6 +18,17 @@ Rogue Dex is a browser extension that connects to Pokerogue and uses PokeAPI to - The extension will display Pokemon information for each round. - Use the data to strategize your gameplay effectively. +## Development +- After cloning the repository, run `npm install` in a console/terminal (after navigating to the repo directory). +- Husky will run pre-commit hooks if enabled, read more: https://typicode.github.io/husky/how-to.html, currently disabled. +- `Package.json` has a bunch of scripts to lint and format code, css and html files. These can be run by a pre-commit hook or manually, for example `npm run + + diff --git a/src/options/options.js b/src/options/options.js new file mode 100644 index 0000000..206f237 --- /dev/null +++ b/src/options/options.js @@ -0,0 +1,117 @@ +/** + * @fileoverview Manages the initialization of options, event listeners, and UI updates for the extension options page. + * @file 'src/options/options.js' + */ + +import OptionsManager from './optionsManager.js'; +import ImageInjector from './imageInjector.js'; +import generateSettingsUI from './generateSettingsUI.js'; + +/** + * Initializes the settings UI, sets up event listeners, and restores previous options. + */ +document.addEventListener('DOMContentLoaded', () => { + generateSettingsUI(); + + /** @type {OptionsManager} */ + const optionsManager = new OptionsManager(); + /** @type {ImageInjector} */ + const imageInjector = new ImageInjector(); + + /** @type {number} */ + let focusedSettingIndex = 0; + /** @type {NodeListOf} */ + const settings = document.querySelectorAll('.input-item'); + + /** + * Updates the focus of the settings UI based on the current focusedSettingIndex. + */ + const updateFocus = () => { + settings.forEach((setting, index) => { + const label = setting.querySelector('.setting-label'); + if (index === focusedSettingIndex) { + label.classList.add('focus'); + } else { + label.classList.remove('focus'); + } + }); + }; + + /** + * Handles the change of an option for a given setting. + * @param {Element} setting - The setting element containing the options. + * @param {string} directionOrValue - The direction ('left', 'right') or specific value to change to. + */ + const handleOptionChange = (setting, directionOrValue) => { + const options = setting.querySelectorAll('.option'); + let selectedIndex = Array.from(options).findIndex(option => option.classList.contains('selected')); + if (directionOrValue === 'left' && selectedIndex > 0) { + selectedIndex -= 1; + } else if (directionOrValue === 'right' && selectedIndex < options.length - 1) { + selectedIndex += 1; + } else if (directionOrValue !== "left" && directionOrValue !== "right") { + selectedIndex = Array.from(options).findIndex(option => option.getAttribute('data-value') === directionOrValue); + } + options.forEach(option => option.classList.remove('selected')); + options[selectedIndex].classList.add('selected'); + + const option = options[selectedIndex]; + const settingName = option.getAttribute('data-setting'); + const value = option.getAttribute('data-value'); + + optionsManager.saveOption(settingName, value); + + if (settingName === 'menuType') { + imageInjector.injectImages(parseInt(value, 10)); + } else if (settingName === 'scaleFactor') { + optionsManager.updateScale(value); + } + }; + + /** + * Handles keyboard navigation for changing settings and options. + * @param {KeyboardEvent} event - The keyboard event. + */ + document.addEventListener('keydown', (event) => { + switch (event.key) { + case 'ArrowUp': + if (focusedSettingIndex > 0) { + focusedSettingIndex -= 1; + updateFocus(); + } + break; + case 'ArrowDown': + if (focusedSettingIndex < settings.length - 1) { + focusedSettingIndex += 1; + updateFocus(); + } + break; + case 'ArrowLeft': + handleOptionChange(settings[focusedSettingIndex], 'left'); + break; + case 'ArrowRight': + handleOptionChange(settings[focusedSettingIndex], 'right'); + break; + } + }); + + /** + * Attach click event listeners to the options for handling option changes. + */ + document.querySelectorAll('.setting-options .option').forEach(option => { + option.addEventListener('click', (event) => { + const setting = event.target.closest('.input-item'); + focusedSettingIndex = Array.from(settings).indexOf(setting); + updateFocus(); + handleOptionChange(setting, event.target.getAttribute('data-value')); + }); + }); + + /** + * Restores the options from storage and updates the UI. + */ + optionsManager.restoreOptions((menuType) => { + imageInjector.injectImages(menuType); + updateFocus(); + }); +}); diff --git a/src/options/optionsManager.js b/src/options/optionsManager.js new file mode 100644 index 0000000..27fd53f --- /dev/null +++ b/src/options/optionsManager.js @@ -0,0 +1,146 @@ +/** + * @fileoverview + * This class manages options for the extension settings. + * @file 'src/options/optionsManager.js' + * @class OptionsManager + */ + +import settingsTemplate from './settingsTemplate.js'; + +class OptionsManager { + constructor() { + this.browserApi = typeof browser !== "undefined" ? browser : chrome; + } + + /** + * Saves an option setting. + * @param {string} setting - The setting to save. + * @param {string|number} value - The value of the setting. + */ + saveOption(setting, value) { + const settings = {}; + if (setting === 'menuType' || setting === 'scaleFactor' || setting === 'sidebarScaleFactor' || setting === 'bottompanelScaleFactor' || setting === 'sidebarCondenseBreakpoint' || setting === 'sidebarHideAlliesBreakpoint' || setting === 'overlayOpacity') { + settings[setting] = parseFloat(value); + } else if (value === 'true' || value === 'false') { + settings[setting] = value === 'true'; + } else { + settings[setting] = value; + } + this.browserApi.storage.sync.set(settings, () => { + if (this.browserApi.runtime.lastError) { + console.error('Error saving option:', this.browserApi.runtime.lastError); + } else { + console.log('Option saved successfully'); + } + }); + } + + /** + * Restores options from storage and updates UI accordingly. + * @param {Function} callback - The function to call after restoring options. + */ + restoreOptions(callback) { + const keys = Object.values(settingsTemplate).map(setting => setting.localStorage); + this.browserApi.storage.sync.get(keys, (data) => { + if (this.browserApi.runtime.lastError) { + console.error('Error retrieving options:', this.browserApi.runtime.lastError); + } else { + document.querySelectorAll('.setting-options .option').forEach(option => { + // Get the setting and value from the data attributes of the current option + const setting = option.getAttribute('data-setting'); + const value = option.getAttribute('data-value'); + + let parsedValue; + // Determine how to parse the value based on the setting type + if ( setting === 'menuType' || setting === 'scaleFactor' || setting === 'sidebarScaleFactor' || setting === 'bottompanelScaleFactor' || setting === 'sidebarCondenseBreakpoint' || setting === 'sidebarHideAlliesBreakpoint' || setting === 'overlayOpacity') { + // Parse numeric values as floats + parsedValue = parseFloat(value); + } + else { + // Convert 'true'/'false' strings to boolean + parsedValue = value === 'true'; + } + + // Check if the current option should be selected + const isSelected = data[setting] === parsedValue || ( (setting === 'sidebarPosition' || setting === 'statusbarPosition' ) && data[setting] === value); + + // Add the 'selected' class if the option matches the setting value + if (isSelected) { + option.classList.add('selected'); + } + }); + + if (callback) { + callback(data.menuType || 1); + } + } + }); + } + + /** + * Updates scale UI elements. + * @param {number} value - The scale value. + */ + updateScale(value) { + const scaleFactor = parseFloat(value); + document.getElementById('scaleValue').textContent = scaleFactor; + // Update any other necessary elements based on scale factor + } + + /** + * Scales UI elements. + */ + scaleElements() { + const manualScaleFactor = document.getElementById('scaleSlider').value; + document.getElementById('scaleValue').textContent = manualScaleFactor; + } + + /** + * Saves options to storage. + */ + saveOptions() { + const disableSettingsHint = document.querySelector('.option[data-setting="disableSettingsHint"].selected').getAttribute('data-value') === 'true'; + const showMin = document.querySelector('.option[data-setting="showMinified"].selected').getAttribute('data-value') === 'true'; + const showMiniCardTypes = document.querySelector('.option[data-setting="showMiniCardTypes"].selected').getAttribute('data-value') === 'true'; + const overlayOpacity = parseInt(document.querySelector('.option[data-setting="overlayOpacity"].selected').getAttribute('data-value'), 10); + const showEnemy = document.querySelector('.option[data-setting="showEnemies"].selected').getAttribute('data-value') === 'true'; + const showParty = document.querySelector('.option[data-setting="showParty"].selected').getAttribute('data-value') === 'true'; + const scaleFactor = document.getElementById('scaleSlider').value; + const statusbarPosition = document.querySelector('.option[data-setting="statusbarPosition"].selected').getAttribute('data-value') === 'true'; + const menuType = parseInt(document.querySelector('.option[data-setting="menuType"].selected').getAttribute('data-value'), 10); + const showSidebar = document.querySelector('.option[data-setting="showSidebar"].selected').getAttribute('data-value') === 'true'; + const sidebarPosition = document.querySelector('.option[data-setting="sidebarPosition"].selected').getAttribute('data-value') === 'true'; + const sidebarScaleFactor = parseFloat(document.querySelector('.option[data-setting="sidebarScaleFactor"].selected').getAttribute('data-value')); + const sidebarCompactTypes = document.querySelector('.option[data-setting="sidebarCompactTypes"].selected').getAttribute('data-value') === 'true'; + const bottompanelScaleFactor = parseFloat(document.querySelector('.option[data-setting="bottompanelScaleFactor"].selected').getAttribute('data-value')); + const sidebarCondenseBreakpoint = parseInt(document.querySelector('.option[data-setting="sidebarCondenseBreakpoint"].selected').getAttribute('data-value'), 10); + const sidebarHideAlliesBreakpoint = parseInt(document.querySelector('.option[data-setting="sidebarHideAlliesBreakpoint"].selected').getAttribute('data-value'), 10); + + this.browserApi.storage.sync.set({ + disableSettingsHint, + showMinified: showMin, + showMiniCardTypes, + overlayOpacity, + scaleFactor, + showEnemies: showEnemy, + showParty, + statusbarPosition, + menuType, + showSidebar, + sidebarPosition, + sidebarScaleFactor, + sidebarCompactTypes, + bottompanelScaleFactor, + sidebarCondenseBreakpoint, + sidebarHideAlliesBreakpoint, + }, () => { + if (this.browserApi.runtime.lastError) { + console.error('Error saving options:', this.browserApi.runtime.lastError); + } else { + console.log('Options saved successfully'); + } + }); + } +} + +export default OptionsManager; diff --git a/src/options/settingsTemplate.js b/src/options/settingsTemplate.js new file mode 100644 index 0000000..c3f9e80 --- /dev/null +++ b/src/options/settingsTemplate.js @@ -0,0 +1,119 @@ +/** + * @typedef {Object} Setting + * @property {string} text - The description of the setting. + * @property {string[]} options - The available options for the setting. + * @property {string} localStorage - The key used for storing the setting in local storage. + * @property {string} type - The data type of the setting's value. + * @property {string} [appendText] - Optional text to append to the option value when displayed. + */ + +/** + * A template for settings configuration. Each key represents a setting with its + * description, options, and other relevant information. + * + * @type {Object} + */ +const settingsTemplate = { + 0: { + text: "Disable Settings Menu Hint", + options: ["Yes", "No"], + localStorage: "disableSettingsHint", + type: "Bool", + + }, + 1: { + text: "Use Minified Overlay Cards", + options: ["No", "Yes"], + localStorage: "showMinified", + type: "Bool" + }, + 2: { + text: "Minified Cards: Show Types", + options: ["No", "Yes"], + localStorage: "showMiniCardTypes", + type: "Bool" + }, + 3: { + text: "Overlay Opacity", + options: ["100", "80", "70", "60", "50", "40", "25"], + localStorage: "overlayOpacity", + type: "Int" + }, + 4: { + text: "Show Enemy Party", + options: ["No", "Yes"], + localStorage: "showEnemies", + type: "Bool" + }, + 5: { + text: "Show Ally Party", + options: ["No", "Yes"], + localStorage: "showParty", + type: "Bool" + }, + 6: { + text: "Overlay Scale", + options: ["0.4", "0.6", "0.8", "1.0", "1.25", "1.5", "2.0"], + appendText: "x", + localStorage: "scaleFactor", + type: "Float" + }, + 7: { + text: "Status Bar Position", + options: ["Top", "Bottom"], + localStorage: "statusbarPosition", + type: "String" + }, + 8: { + text: "Menu Type", + options: ["1", "2", "3", "4", "5"], + localStorage: "menuType", + type: "Int" + }, + 9: { + text: "Show Sidebar", + options: ["No", "Yes"], + localStorage: "showSidebar", + type: "Bool" + }, + 10: { + text: "Sidebar Position", + options: ["Left", "Right"], + localStorage: "sidebarPosition", + type: "String" + }, + 11: { + text: "Sidebar Scale", + options: ["0.4", "0.6", "0.8", "1.0", "1.25", "1.5", "2.0"], + appendText: "x", + localStorage: "sidebarScaleFactor", + type: "Float" + }, + 12: { + text: "Side: Compact TypeEffectiveness ", + options: ["No", "Yes"], + localStorage: "sidebarCompactTypes", + type: "Bool" + }, + 13: { + text: "Side: Small view at # of Pokemon", + options: ["9", "11", "12", "100"], + localStorage: "sidebarCondenseBreakpoint", + type: "Int" + }, + 14: { + text: "Side: Hide Allies at # of Pokemon", + options: ["9", "11", "12", "100"], + localStorage: "sidebarHideAlliesBreakpoint", + type: "Int" + }, + 15: { + text: "Bottom Panel Scale", + options: ["0.4", "0.6", "0.8", "1.0", "1.25", "1.5", "2.0"], + appendText: "x", + localStorage: "bottompanelScaleFactor", + type: "Float" + } +}; + +export default settingsTemplate; \ No newline at end of file diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..dc83544 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,1680 @@ +@font-face { + font-family: 'pokemon-emerald-pro'; + src: url('../fonts/pokemon-emerald-pro.ttf') format('truetype'); +} + +:root { + --root-font-size: 62.5%; + --icon-size: 2.4em; + --card-icon-size: 3.5em; + --card-icon-mini-size: 2em; + --type-effectiveness-border-width: 0.2em; + --type-effectiveness-item-margin: 0.1em; + + --sidebar-img-max-height: 9em; + --card-img-max-height: 15em; + --card-img-mini-max-height: 8em; + --sidebar-borders-color: rgb(59, 59, 59); + --sidebar-bg-color: #2b2d31; + --enemy-border-color: #b30a21; + --ally-border-color: #0095ff; + --sidebar-font: 'pokemon-emerald-pro'; + --overlay-font: 'pokemon-emerald-pro'; + --text-shadow-dark: 0.1em 0 rgba(255, 255, 255, 0), + -0.1em 0 rgba(255, 255, 255, 0), 0 0.1em rgba(255, 255, 255, 0), + 0 -0.1em rgba(255, 255, 255, 0), 0.05em 0.05em rgba(255, 255, 255, 0), + -0.05em -0.05em rgba(255, 255, 255, 0), + 0.05em -0.05em rgba(255, 255, 255, 0), + -0.05em 0.05em rgba(255, 255, 255, 0); + --text-shadow-light: 0.1em 0 rgba(255, 255, 255, 0.9), + -0.1em 0 rgba(255, 255, 255, 0.9), 0 0.1em rgba(255, 255, 255, 0.9), + 0 -0.1em rgba(255, 255, 255, 0.9), + 0.05em 0.05em rgba(255, 255, 255, 0.9), + -0.05em -0.05em rgba(255, 255, 255, 0.9), + 0.05em -0.05em rgba(255, 255, 255, 0.9), + -0.05em 0.05em rgba(255, 255, 255, 0.9); + /* PLACEHOLDERS, set programmatically */ + --extension-rarity-bg-image-sparkles: url('__EXTENSION_IMAGE_1_URL__'); + --extension-rarity-bg-image-holo: url('__EXTENSION_IMAGE_2_URL__'); +} + +html { + font-size: var(--root-font-size); +} + +html body #tnc-links { + /* pokerogue css overwrite */ + display: block !important; + position: absolute !important; + bottom: 0 !important; + left: 0 !important; + z-index: 10 !important; +} +html body #tnc-links.sidebar-Left { + left: 30% !important; + right: unset !important; +} +html body #tnc-links.sidebar-Right { + right: 30% !important; + left: unset !important; +} + +body { + font-size: 1rem; /* 10px, default font-size, but percentage based calculation */ + display: flex; /* pokerogue css overwrite */ +} +@media screen and (max-width: 7680px) { + body { + font-size: 4rem; + } +} +@media screen and (max-width: 5120px) { + body { + font-size: 2.5rem; + } +} +@media screen and (max-width: 3840px) { + body { + font-size: 2rem; + } +} +@media screen and (max-width: 2560px) { + body { + font-size: 1.4rem; + } +} +@media screen and (max-width: 1920px) { + body { + font-size: 1rem; + } +} +@media screen and (max-width: 1440px) { + body { + font-size: 0.8rem; + } +} +@media screen and (max-width: 1024px) { + body { + font-size: 0.55rem; + } +} + +#app { + display: block; + height: fit-content; +} +/* apply to game app when sidebar is visible */ +#app.sidebar-active { + max-width: 70%; + float: right; +} + +#rd-settings-hint { + position: absolute; + left: 5em; + top: 0.1em; + font-size: 1em; + padding: 0.2em; + border-radius: 50%; + border: 0.1em solid black; + background-color: white; + z-index: 20; +} +#rd-settings-hint.disabled { + display: none; +} +#rd-settings-hint .rd-hint-icon { + font-size: 3em; + line-height: 0.8; +} +#rd-settings-hint .tooltiptext { + width: max-content; + padding: 1.5em; +} +#rd-settings-hint .tooltiptext span { + font-family: verdana, arial, serif; + font-size: 1.5em; +} + +/* Pokemon Cards (Overlay) display state handling + .active-because-sidebar-hidden + .disabled = sidebar not shown but card hidden by user choice + .hidden-because-sidebar-active = all cards hidden because sidebar is active +*/ +#enemies.active-because-sidebar-hidden.disabled, +#enemies.disabled, +#allies.active-because-sidebar-hidden.disabled, +#allies.disabled { + display: none; +} +#enemies.hidden-because-sidebar-active, +#allies.hidden-because-sidebar-active { + display: none; +} + +/* roguedex sidebar */ +.roguedex-sidebar { + float: left; + height: 100%; + width: 30%; + max-width: 30%; + background-color: var(--sidebar-bg-color); + border: 0.1em solid var(--sidebar-borders-color); + overflow-y: auto; + overflow-x: hidden; + display: none; + box-sizing: border-box; + position: relative; + font-family: var(--sidebar-font); + line-height: 100%; + font-size: 1em; /* base font size for all em values in sidebar */ +} + +.roguedex-sidebar.active { + display: block; + overflow-y: clip; +} +.roguedex-sidebar.hidden { + display: none; +} +body.sidebar-Left { + flex-direction: row; +} +body.sidebar-Right { + flex-direction: row-reverse; +} +body.sidebar-Top { + flex-direction: column; +} +body.sidebar-Bottom { + flex-direction: column-reverse; +} +#sidebar-switch-iv-moves { + position: absolute; + padding: 0; + font-size: inherit; + line-height: 100%; + margin: 0; + color: white; + + top: -0.1em; + left: -0.1em; + width: 3.1em; + height: 3em; + line-height: 1; + border-radius: 0; + border: 0.1em solid rgba(88, 88, 88, 0.859); + background-color: rgba(0, 0, 0, 0.3); +} +#sidebar-switch-iv-moves:hover { + background-color: rgba(0, 0, 0, 0.5); + border-color: var(--ally-border-color); + cursor: pointer; +} +#sidebar-switch-iv-moves:active { + transform: scale(0.95); + background-color: rgba(0, 0, 0, 0.4); +} +#sidebar-switch-iv-moves span { + font-size: 1.8em; +} +#sidebar-switch-iv-moves.hidden { + display: none; +} +#sidebar-switch-iv-moves.visible { + display: block; +} + +#go-to-options { + position: absolute; + font-size: 1.8em; + padding: 0; + width: 2.4em; + height: 2.4em; + top: 0.3em; + right: 0.3em; + margin: 0; + background-color: #5c5c5c; + color: white; + border-radius: 0.3em; + border: 0.1em solid var(--sidebar-borders-color); +} + +.roguedex-sidebar .sidebar-header { + color: white; + width: 100%; + text-align: center; + font-family: inherit; + height: 3em; + line-height: 3em; +} +.roguedex-sidebar .sidebar-header span { + font-size: 3em; +} +.roguedex-sidebar .sidebar-header-trainer-battle { + color: #ff7e7e; + font-size: 2.2em; + padding-left: 1em; +} +.roguedex-sidebar .tooltip .tooltiptext span { + font-size: 2em !important; + line-height: 0.8 !important; + padding: 0 0.3em !important; +} + +.enemies-party, +.allies-party { + display: table; + color: white; + font-family: inherit; + border-top: 0.1em solid var(--sidebar-borders-color); + margin: 0; + width: 100%; + box-sizing: border-box; +} +.enemies-party { + --party-border-width: 0.3em; + border-width: var(--party-border-width); + border-style: solid; + border-image: linear-gradient( + to right, + var(--enemy-border-color), + var(--sidebar-bg-color) + ) + 1; + border-top: none; + border-right: none; +} +.allies-party { + --party-border-width: 0.3em; + border-width: var(--party-border-width); + border-style: solid; + border-image: linear-gradient( + to right, + var(--ally-border-color), + var(--sidebar-bg-color) + ) + 1; + border-bottom: none; + border-right: none; +} +.sidebar-allies-box.hidden, +.sidebar-enemies-box.hidden { + display: none; +} +.sidebar-Right .enemies-party { + --party-border-width: 0.3em; + border-width: var(--party-border-width); + border-style: solid; + border-image: linear-gradient( + to left, + var(--enemy-border-color), + var(--sidebar-bg-color) + ) + 1; + border-top: none; + border-left: none; +} +.sidebar-Right .allies-party { + --party-border-width: 0.3em; + border-width: var(--party-border-width); + border-style: solid; + border-image: linear-gradient( + to left, + var(--ally-border-color), + var(--sidebar-bg-color) + ) + 1; + border-bottom: none; + border-left: none; +} +.pokemon-entry { + display: table-row; +} +.pokemon-entry:nth-child(odd) { + background-color: #1e1f22; +} +.pokemon-entry:nth-child(even) { + background-color: #1e1f22; +} +.pokemon-entry-image, +.pokemon-type-effectiveness-wrapper, +.pokemon-info-text-wrapper { + display: table-cell; + border-bottom: 0.1em solid var(--sidebar-borders-color); + padding: 0.5em 0.5em 0.5em 0; + vertical-align: top; +} +.pokemon-type-effectiveness-wrapper { + padding-right: 0.7em !important; +} +.pokemon-entry .pokemon-entry-image { + padding: 0.5em 0.5em; + vertical-align: middle; + position: relative; +} +.pokemon-entry-image.tooltip .tooltiptext, .pokemon-type-icon-wrapper.tooltip .tooltiptext { + padding: 0.4em 0.6em; + text-align: left; + width: max-content; + z-index: 10; +} +.pokemon-entry-image.tooltip .tooltiptext span { + display: contents; + text-shadow: none; +} +.pokemon-entry:last-child.div { + border-bottom: none; +} +.roguedex-sidebar .pokemon-type-icon { + width: var(--icon-size); + height: var(--icon-size); + background-size: cover; + background-repeat: no-repeat; + background-position: left; + background-position-x: -0.07em; + border: none; + border-radius: 0; + margin: 0; +} +.roguedex-sidebar .pokemon-type-icon-wrapper { + border-radius: 50%; + margin: 0.1em 0.1em; + + -webkit-box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + -moz-box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + + overflow: hidden; +} +.roguedex-sidebar .pokemon-type-weaknesses, +.roguedex-sidebar .pokemon-type-resistances, +.roguedex-sidebar .pokemon-type-immunities { + border-width: var(--type-effectiveness-border-width); + min-width: min-content; + max-width: inherit; + display: flex; + flex-wrap: wrap; + min-height: calc(var(--icon-size) * 1); + min-width: calc(var(--icon-size) * 3); + border-top-right-radius: 0.5em; + border-bottom-right-radius: 0.5em; + border-top-left-radius: 0.5em; + border-bottom-left-radius: 0.5em; + + position: relative; + padding: 0.1em 0.1em 0.1em 0.1em; + width: -webkit-fill-available; +} +.roguedex-sidebar .pokemon-type-weaknesses { + background: rgb(36, 0, 0); + background: linear-gradient( + 90deg, + rgb(71, 0, 0) 0%, + rgba(154, 2, 2, 1) 12%, + rgba(255, 0, 0, 0.17) 100% + ); + border: 0.1em solid rgb(192, 2, 2); +} +.roguedex-sidebar .pokemon-type-resistances { + background: rgb(0, 36, 7); + background: linear-gradient( + 90deg, + rgb(0, 75, 15) 0%, + rgba(13, 154, 2, 1) 12%, + rgba(1, 255, 0, 0.17) 100% + ); + border: 0.1em solid rgb(14, 207, 1); +} +.roguedex-sidebar .pokemon-type-immunities { + background: rgb(68, 68, 68); + background: linear-gradient( + 90deg, + rgba(68, 68, 68, 1) 0%, + rgba(115, 115, 115, 1) 12%, + rgba(161, 161, 161, 0.17) 100% + ); + border: 0.1em solid rgb(148, 148, 148); +} +.roguedex-sidebar .pokemon-type-weaknesses::before, +.roguedex-sidebar .pokemon-type-resistances::before, +.roguedex-sidebar .pokemon-type-immunities::before { + display: none; + width: 1em; + margin-right: 1em; + vertical-align: middle; + text-align: center; + border-top-left-radius: 0.5em; + border-bottom-left-radius: 0.5em; + + position: absolute; + top: 0; + bottom: 0; + left: 0; +} +.roguedex-sidebar .pokemon-type-weaknesses::before { + content: ' '; + background-color: rgb(36, 0, 0); + border: 0.1em solid rgb(192, 2, 2); +} +.roguedex-sidebar .pokemon-type-resistances::before { + content: ' '; + background-color: rgb(0, 36, 7); + border: 0.1em solid rgb(14, 207, 1); +} +.roguedex-sidebar .pokemon-type-immunities::before { + content: ' '; + background-color: rgb(68, 68, 68); + border: 0.1em solid rgb(148, 148, 148); +} +.roguedex-sidebar .pokemon-entry-image canvas { + height: var(--sidebar-img-max-height); + width: var(--sidebar-img-max-height); +} +.canvas-fallback-text { + /* todo */ + display: list-item; + padding: 0.3em; + position: absolute; + top: 20px; + font-size: 1.3em; + box-sizing: border-box; + max-width: var(--sidebar-img-max-height); + left: 0px; + color: rgb(184, 184, 184); +} + +.pokemon-info-text-wrapper { + padding: 0.2em 0.2em 0.2em 0; + line-height: 0.9; +} +.pokemon-info-text-wrapper .pokemon-ability-description, +.pokemon-info-text-wrapper .pokemon-nature-description, +.pokemon-info-text-wrapper .pokemon-ability-value, +.pokemon-info-text-wrapper .pokemon-nature-value, +.pokemon-info-text-wrapper .text-base .tooltiptext, +.pokemon-info-text-wrapper .stat-p span, +.pokemon-info-text-wrapper .pokemon-move-name { + /* set font-sizes on lowest child elements to not mess up em scaling */ + font-size: 2.5em; +} + +.roguedex-sidebar.hideIVs .allies-party .pokemon-ivs, +.roguedex-sidebar.hideMoveset .allies-party .pokemon-moveset-wrapper { + display: none; +} +.roguedex-sidebar .pokemon-ivs, +.roguedex-sidebar .pokemon-ability-nature { + display: flex; + flex-wrap: wrap; +} +.roguedex-sidebar .pokemon-ability, +.roguedex-sidebar .pokemon-nature { + display: flex; + padding: 0 1em 0 0; +} +.roguedex-sidebar .stat-p { + display: flex; + padding: 0 1em 0 0; + position: relative; +} +.roguedex-sidebar .stat-p span { + display: inline-block; + padding: 0 0.3em 0 0; +} +.roguedex-sidebar .stat-c { + padding-right: 0.4em; + position: relative; + text-shadow: var(--text-shadow-dark); +} +.roguedex-sidebar .stat-icon { + padding: 0; + position: absolute; + top: -0.2em; + right: -0.15em; + opacity: 0.7 !important; +} +.roguedex-sidebar .stat-p.stat-p-colors { + color: #adadad; +} +.roguedex-sidebar .stat-c.stat-c-colors { + color: white; +} + +.roguedex-sidebar .pokemon-ability-description, +.roguedex-sidebar .pokemon-nature-description { + color: #adadad; +} +.roguedex-sidebar .pokemon-ability-value, +.roguedex-sidebar .pokemon-nature-value { + color: white; + padding: 0 0 0 0.4em; +} +.roguedex-sidebar .pokemon-ability.hidden-ability .pokemon-ability-value { + color: rgb(249 79 255); +} +.pokemon-type-icon.super-dmg, +.pokemon-type-icon.super-resist { + animation: pulsate 1s ease-out; + animation-iteration-count: infinite; + opacity: 1; +} +@keyframes pulsate { + 0% { + -webkit-transform: scale(0.9, 0.9); + opacity: 0.8; + } + 50% { + opacity: 1; + } + 100% { + -webkit-transform: scale(1.1, 1.1); + opacity: 0.8; + } +} +@keyframes opacityPulse { + 0% { + opacity: 0.5; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0.5; + } +} +.pokemon-moveset-wrapper { + display: flex; + flex-wrap: wrap; + margin: 0.3em 0.3em 0 0; +} +.pokemon-moveset-wrapper .pokemon-move { + padding: 0 0.5em 0 0; + flex-basis: 50%; + box-sizing: border-box; + text-shadow: var(--text-shadow-dark); +} +.pokemon-moveset-wrapper .pokemon-move .tooltip .tooltiptext { + font-size: 0.4em; +} + +.pokemon-moveset-wrapper .pokemon-move-name.move-normal { + color: rgb(170 170 153); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-fire { + color: rgb(255 68 34); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-water { + color: rgb(51 153 255); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-electric { + color: rgb(255 204 51); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-grass { + color: rgb(119 204 85); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-ice { + color: rgb(102 204 255); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-fighting { + color: rgb(187 85 68); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-poison { + color: rgb(170 85 153); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-ground { + color: rgb(187 85 68); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-flying { + color: rgb(136 153 255); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-psychic { + color: rgb(255 85 153); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-bug { + color: rgb(170 187 34); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-rock { + color: rgb(187 170 102); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-ghost { + color: rgb(102 102 187); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-dragon { + color: rgb(119 102 238); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-dark { + color: rgb(119 85 68); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-steel { + color: rgb(170 170 187); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-fairy { + color: rgb(238 153 238); +} +.pokemon-moveset-wrapper .pokemon-move-name.move-stellar { + color: white; +} + +/* Compact sidebar type-effectiveness display */ +.roguedex-sidebar.compactTypeDisplay + .pokemon-type-effectiveness-wrapper.compact { + display: table-cell; +} +.roguedex-sidebar.defaultTypeDisplay + .pokemon-type-effectiveness-wrapper.compact { + display: none; +} +.roguedex-sidebar.defaultTypeDisplay + .pokemon-type-effectiveness-wrapper.default { + display: table-cell; +} +.roguedex-sidebar.compactTypeDisplay + .pokemon-type-effectiveness-wrapper.default { + display: none; +} + +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-category, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-category { + border-width: var(--type-effectiveness-border-width); + max-width: inherit; + display: flex; + flex-wrap: wrap; + min-height: calc(var(--icon-size) * 1); + border-radius: 0; + + position: relative; + padding: 0.2em 0.2em 0.2em 0.2em; + + min-width: fit-content; + width: fit-content; + border-width: 0.1em; + border-style: solid; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-weaknesses, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-weaknesses { + background: #810e0e; + border: 0.1em solid rgb(192, 2, 2); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-resistances, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-resistances { + background: rgba(76, 185, 68, 1); + border: 0.1em solid rgb(92, 229, 82, 1); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-immunities, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-immunities { + background: rgb(97, 96, 96); + border: 0.1em solid rgb(148, 148, 148); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-category:before, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-category:before { + /* todo, still used? */ + display: none; + width: 1em; + margin-right: 1em; + vertical-align: middle; + text-align: center; + border-top-left-radius: 0.5em; + border-bottom-left-radius: 0.5em; + + position: absolute; + top: 0; + bottom: 0; + left: 0; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-weaknesses::before, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-weaknesses::before { + /* todo, still used? */ + content: ' '; + background-color: rgb(36, 0, 0); + border-color: 0.1em solid rgb(192, 2, 2); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-resistances::before, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-resistances::before { + /* todo, still used? */ + content: ' '; + background-color: rgb(0, 36, 7); + border-color: 0.1em solid rgb(14, 207, 1); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-immunities::before, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .pokemon-type-immunities::before { + /* todo, still used? */ + content: ' '; + background-color: rgb(68, 68, 68); + border-color: 0.1em solid rgb(148, 148, 148); +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-block, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-block { + display: flex; + flex-direction: column; + width: fit-content; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row { + display: flex; + justify-content: flex-start; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(2n), +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(2n) { + flex-direction: row-reverse; + justify-content: end; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(2n) + .type-effectiveness-category, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(2n) + .type-effectiveness-category { + border-top: none !important; +} +.roguedex-sidebar + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(n + 2) + .type-effectiveness-category, +.pokemon-card + .pokemon-type-effectiveness-wrapper.compact + .type-effectiveness-row:nth-child(n + 2) + .type-effectiveness-category { + border-top: none !important; +} +.roguedex-sidebar .pokemon-type-effectiveness-wrapper.compact .transp-right, +.pokemon-card .pokemon-type-effectiveness-wrapper.compact .transp-right { + border-right-color: transparent !important; +} +.roguedex-sidebar .pokemon-type-effectiveness-wrapper.compact .transp-bottom, +.pokemon-card .pokemon-type-effectiveness-wrapper.compact .transp-bottom { + border-bottom-color: transparent !important; +} +.roguedex-sidebar .pokemon-type-effectiveness-wrapper.compact .transp-left, +.pokemon-card .pokemon-type-effectiveness-wrapper.compact .transp-left { + border-left-color: transparent !important; +} + +/* change view for trainer battles */ +.pokemon-entry.condensed { + display: table-cell; + border: 0.1em solid black; +} +.pokemon-entry.condensed .pokemon-type-effectiveness-wrapper.compact { + display: none !important; +} +.pokemon-entry.condensed .pokemon-type-effectiveness-wrapper { + display: contents !important; +} + +.pokemon-entry.condensed .pokemon-entry-image { + display: block; + text-align: center; +} +.pokemon-entry.condensed .pokemon-type-effectiveness-wrapper { + display: contents; +} +.pokemon-entry.condensed .allies-party, +.pokemon-entry.condensed .enemies-party { + display: flex; +} +.pokemon-entry.condensed .pokemon-info-text-wrapper { + display: none; +} + +/* sidebar end */ + +/* bottom panel */ + +.roguedex-bottom-panel { + line-height: 100%; + bottom: 0; + position: absolute; + z-index: 0; + background-color: var(--sidebar-bg-color); + width: 100%; + height: 100%; + box-sizing: content-box; + display: none; + border-bottom: 0.1em solid var(--sidebar-borders-color); + font-family: var(--sidebar-font); + overflow: hidden; + font-size: 1em; /* base font size for all em values in sidebar */ +} +.roguedex-bottom-panel.sidebar-active { + display: block; +} +.roguedex-bottom-panel.sidebar-Left { + right: 0; +} +.roguedex-bottom-panel.sidebar-Right { + left: 0; +} +.roguedex-bottom-panel-content { + padding: 0.2em; + height: -webkit-fill-available; + color: white; + font-family: inherit; + font-size: inherit; +} +.roguedex-bottom-panel-header { + font-size: inherit; +} +.bottom-panel-weather-box { + margin: 0.5em; +} +.bottom-panel-party-modifiers td, +.bottom-panel-enemy-modifiers td, +.bottom-panel-pokemon-modifiers td { + font-size: 2.5em; + padding: 0 0.2em; + line-height: 0.9; +} +.bottom-panel-party-modifiers td:nth-child(2n), +.bottom-panel-enemy-modifiers td:nth-child(2n), +.bottom-panel-pokemon-modifiers td:nth-child(2n) { + text-align: right; +} +.bottom-panel-party-modifiers td:nth-child(2), +.bottom-panel-enemy-modifiers td:nth-child(2), +.bottom-panel-pokemon-modifiers td:nth-child(2) { + padding-right: 0.7em; +} +.bottom-panel-party-modifiers, +.bottom-panel-enemy-modifiers, +.bottom-panel-pokemon-modifiers { + text-align: left; + background-color: #25272b; + border: 0.1em solid rgb(26, 25, 25); + float: left; +} +.bottom-panel-party-modifiers, +.bottom-panel-pokemon-modifiers { + margin-right: 0.1em; +} +.bottom-panel-party-modifiers .zeroValue, +.bottom-panel-enemy-modifiers .zeroValue, +.bottom-panel-pokemon-modifiers .zeroValue { + color: rgb(104, 104, 104); +} +.bottom-panel-party-modifiers { + border-width: 0.4em; + border-style: solid; + border-top-style: solid; + border-right-style: solid; + border-image: linear-gradient( + to bottom, + var(--ally-border-color), + var(--sidebar-bg-color) + ) + 1; + border-bottom: none; + border-left: none; + border-top: none; + position: relative; +} +.bottom-panel-party-modifiers::before { + position: absolute; + inset: 0px -1px; + content: ''; + background: transparent; + border-width: 0.3em; + border-style: solid; + border-image: linear-gradient( + to left, + var(--ally-border-color), + var(--sidebar-bg-color) + ) + 1; + border-image-slice: 1; + border-bottom: none; + border-left: none; + border-right: none; + width: -moz-available; +} +.bottom-panel-enemy-modifiers { + border-width: 0.4em; + border-style: solid; + border-top-style: solid; + border-left-style: solid; + border-image: linear-gradient( + to bottom, + var(--enemy-border-color), + var(--sidebar-bg-color) + ) + 1; + border-bottom: none; + border-right: none; + border-top: none; + position: relative; +} +.bottom-panel-enemy-modifiers::before { + position: absolute; + inset: 0px -1px; + content: ''; + background: transparent; + border-width: 0.3em; + border-style: solid; + border-image: linear-gradient( + to right, + var(--enemy-border-color), + var(--sidebar-bg-color) + ) + 1; + border-image-slice: 1; + border-bottom: none; + border-left: none; + border-right: none; + width: -moz-available; +} +.bottom-panel-modifiers-wrapper { + float: left; +} + +.bottom-panel-tabs { + margin-top: 0px; + color: white; +} + +.bottom-panel-tab-buttons { + display: flex; + border-bottom: 0.05em solid var(--sidebar-borders-color); +} + +.bottom-panel-tab-button { + padding: 0.1em 0.2em; + cursor: pointer; + background: none; + outline: none; + font-size: inherit; + transition: background-color 0.3s; + color: white; + font-family: inherit; + border: 0.05em solid var(--sidebar-borders-color); + background-color: rgb(30, 31, 34); + font-size: 2.5em; + line-height: 100%; +} + +.bottom-panel-tab-button:hover { + background-color: rgb(44, 45, 49); +} + +.bottom-panel-tab-button.active { + border-bottom: 0.1em solid #007bff; + background-color: rgb(39, 40, 44); +} + +.bottom-panel-tab-content { + display: none; +} + +.bottom-panel-tab-content.active { + display: block; +} + +.roguedex-sidebar .text-base, +.roguedex-bottom-panel .text-base { + font-family: inherit; +} + +/* bottom panel end */ + +/* legendary, mystical effects + https://codepen.io/simeydotme/pen/PrQKgo + https://github.com/simeydotme/pokemon-cards-css +*/ + +.pokemon-entry-image.pokemon-rarity-legendary:before, +.pokemon-entry-image.pokemon-rarity-legendary:after, +.pokemon-entry-image.pokemon-rarity-mythical:before, +.pokemon-entry-image.pokemon-rarity-mythical:after, +.pokemon-entry-image.pokemon-rarity-ultra:before, +.pokemon-entry-image.pokemon-rarity-ultra:after, +.pokemon-card .pokemon-rarity-legendary:before, +.pokemon-card .pokemon-rarity-legendary:after, +.pokemon-card .pokemon-rarity-ultra:before, +.pokemon-card .pokemon-rarity-ultra:after, +.pokemon-card .pokemon-rarity-mythical:before, +.pokemon-card .pokemon-rarity-mythical:after { + content: ''; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + background-repeat: no-repeat; + opacity: 0.5; + mix-blend-mode: color-dodge; + transition: all 0.33s ease; + border-radius: 0.5em; +} + +.pokemon-entry-image.pokemon-rarity-legendary:before, +.pokemon-card .pokemon-rarity-legendary:before { + --color1: rgb(0, 231, 255); + --color2: rgb(255, 0, 231); + background-position: 50% 50%; + background-size: 300% 300%; + background-image: linear-gradient( + 115deg, + transparent 0%, + var(--color1) 25%, + transparent 47%, + transparent 53%, + var(--color2) 75%, + transparent 100% + ); + opacity: 0.7; + filter: brightness(0.6) contrast(1); + z-index: 1; +} +.pokemon-entry-image.pokemon-rarity-mythical:before, +.pokemon-card .pokemon-rarity-mythical:before { + --color1: rgb(255, 0, 231); + --color2: rgb(229, 254, 44); + background-position: 50% 50%; + background-size: 300% 300%; + background-image: linear-gradient( + 115deg, + transparent 0%, + var(--color1) 25%, + transparent 47%, + transparent 53%, + var(--color2) 75%, + transparent 100% + ); + opacity: 0.7; + filter: brightness(0.6) contrast(1); + z-index: 1; +} +.pokemon-entry-image.pokemon-rarity-ultra:before, +.pokemon-card .pokemon-rarity-ultra:before { + --color1: rgb(229, 254, 44); + --color2: rgb(0, 231, 255); + background-position: 50% 50%; + background-size: 300% 300%; + background-image: linear-gradient( + 115deg, + transparent 0%, + var(--color1) 25%, + transparent 47%, + transparent 53%, + var(--color2) 75%, + transparent 100% + ); + opacity: 0.7; + filter: brightness(0.6) contrast(1); + z-index: 1; +} + +.pokemon-entry-image.pokemon-rarity-legendary:after, +.pokemon-entry-image.pokemon-rarity-mythical:after, +.pokemon-entry-image.pokemon-rarity-ultra:after, +.pokemon-card .pokemon-rarity-legendary:after, +.pokemon-card .pokemon-rarity-mythical:after, +.pokemon-card .pokemon-rarity-ultra:after { + opacity: 1; + background-image: var(--extension-rarity-bg-image-sparkles), + var(--extension-rarity-bg-image-sparkles), + var(--extension-rarity-bg-image-holo), + linear-gradient( + 125deg, + #ff008450 15%, + #fca40040 30%, + #ffff0030 40%, + #00ff8a20 60%, + #00cfff40 70%, + #cc4cfa50 85% + ); + background-position: 50% 50%; + background-size: 360%; + background-blend-mode: overlay; + z-index: 2; + filter: brightness(1) contrast(1); + transition: all 0.33s ease; + mix-blend-mode: color-dodge; + opacity: 0.75; +} + +/* legendary, mystical effects end */ + +/* shiny star, pure css */ + +.four-pointed-star { + position: relative; + margin: 0 auto; + width: 1.4em; + height: 1.4em; + display: inline-block; +} +.four-pointed-star:before { + content: ''; + position: absolute; + /* background: rgba(245, 196, 39, 0.9); */ + background: white; + width: 0.5em; + height: 0.575em; + top: 0.4em; + left: 0.4em; + transform: rotate(-40deg) skewX(32.5deg) skewY(32.5deg); +} +.four-pointed-star:after { + content: ''; + position: absolute; + /* background: rgba(245, 196, 39, 0.9); */ + background: white; + width: 0.5em; + height: 0.575em; + top: 0.4em; + left: 0.4em; + transform: rotate(50deg) skewX(32.5deg) skewY(32.5deg); +} + +/* shiny star, pure css end */ + +/* sidebar + card pokemon info overlay */ + +.sidebar-pokemon-info { + display: block; + position: absolute; + left: 0.5em; + top: 0.5em; + line-height: 1; +} +.sidebar-pokemon-info.allies span { + display: inline-block; + vertical-align: top; + font-size: 1.6em; +} +.sidebar-pokemon-info.enemies span { + line-height: 100%; + text-align: center; + display: inline-block; + vertical-align: top; + font-size: 1.6em; +} +.sidebar-pokemon-info.enemies div.sidebar-pokemon-info-region { + --size: 1.6em; + width: var(--size); + height: var(--size); + display: inline-block; + vertical-align: top; + background-size: contain; + background-repeat: no-repeat; + background-position: center; + background-color: rgba(0, 0, 0, 0.3); +} +.sidebar-pokemon-info.enemies span.sidebar-pokemon-info-paradox, +.sidebar-pokemon-info.enemies span.sidebar-pokemon-info-generation, +.sidebar-pokemon-info.enemies span.sidebar-pokemon-info-rarity { + padding: 0 0.3em; + background: + linear-gradient(to right, #a6afc0 0.1rem, transparent 0.1rem) 0 0, + linear-gradient(to bottom, #a6afc0 0.1rem, transparent 0.1rem) 0 0, + linear-gradient(to left, #a6afc0 0.1rem, transparent 0.1rem) 100% 0, + linear-gradient(to bottom, #a6afc0 0.1rem, transparent 0.1rem) 100% 0, + linear-gradient(to left, #a6afc0 0.1rem, transparent 0.1rem) 100% 100%, + linear-gradient(to top, #a6afc0 0.1rem, transparent 0.1rem) 100% 100%, + linear-gradient(to right, #a6afc0 0.1rem, transparent 0.1rem) 0 100%, + linear-gradient(to top, #a6afc0 0.1rem, transparent 0.1rem) 0 100%; + background-repeat: no-repeat; + background-color: rgba(0, 0, 0, 0.3); + background-size: 0.4em 0.3em; +} + +.card-pokemon-info { + display: block; + position: absolute; + left: 0.4em; + top: 0.4em; + background-color: rgba(0, 0, 0, 0.4); + color: white; + font-family: var(--overlay-font); +} +.card-pokemon-info.enemies span { + font-size: 2em; + line-height: 1; + text-align: center; + display: inline-block; + vertical-align: top; +} +.card-pokemon-info.enemies span.card-pokemon-info-region { + --size: 2em; + width: var(--size); + height: var(--size); + background-size: contain; + background-repeat: no-repeat; + background-position: center; +} +.card-pokemon-info.enemies span.card-pokemon-info-paradox, +.card-pokemon-info.enemies span.card-pokemon-info-generation, +.card-pokemon-info.enemies span.card-pokemon-info-rarity { + padding: 0 0.3em; + background: + linear-gradient(to right, #a6afc0 0.1rem, transparent 0.1rem) 0 0, + linear-gradient(to bottom, #a6afc0 0.1rem, transparent 0.1rem) 0 0, + linear-gradient(to left, #a6afc0 0.1rem, transparent 0.1rem) 100% 0, + linear-gradient(to bottom, #a6afc0 0.1rem, transparent 0.1rem) 100% 0, + linear-gradient(to left, #a6afc0 0.1rem, transparent 0.1rem) 100% 100%, + linear-gradient(to top, #a6afc0 0.1rem, transparent 0.1rem) 100% 100%, + linear-gradient(to right, #a6afc0 0.1rem, transparent 0.1rem) 0 100%, + linear-gradient(to top, #a6afc0 0.1rem, transparent 0.1rem) 0 100%; + background-repeat: no-repeat; + background-size: 0.4em 0.3em; +} + +.card-pokemon-info.tooltip { + z-index: 4; /* hover not possible otherwise because of foil/holo elements */ +} +.card-pokemon-info.tooltip .tooltiptext { + padding: 0.3em 0.4em 0.3em 0.4em; + text-align: left; + width: max-content; + z-index: 10; + font-size: unset; +} +.card-pokemon-info.tooltip .tooltiptext span { + display: contents; + text-shadow: none; + font-size: 2.5em; +} + +/* sidebar + card pokemon info overlay END */ + +.allies-team, +.enemy-team { + font-size: 1em; /* base font size for all em values in overlay cards */ + display: flex; + z-index: 1; + margin: 0 auto; + padding: 0 auto; + position: absolute; +} + +.allies-team { + top: 0; + right: 0; +} + +.enemy-team { + top: 0; +} + +.pokemon-cards { + display: flex; + flex-wrap: nowrap; + overflow: visible; +} + +.pokemon-card { + background-color: rgb(50 50 50 / 70%); + border: 0.2em solid #555; + border-top-left-radius: 0; + border-top-right-radius: 0.8em; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0.8em; + flex: 0 0 auto; + display: flex; + flex-direction: column; + position: relative; +} +#enemies .pokemon-card { + border-left-color: var(--enemy-border-color); +} +#enemies .pokemon-card::before, +#enemies .pokemon-card::after { + border-image: linear-gradient( + to right, + var(--enemy-border-color), + rgba(85, 85, 85, 0) + ) + 1; +} +#allies .pokemon-card { + border-left-color: var(--ally-border-color); +} +#allies .pokemon-card::before, +#allies .pokemon-card::after { + border-image: linear-gradient( + to right, + var(--ally-border-color), + rgba(85, 85, 85, 0) + ) + 1; +} +.pokemon-card::before, +.pokemon-card::after { + position: absolute; + inset: 0px -1px; + height: 0; + content: ''; + background: transparent; + border-width: 0.2em; + border-style: solid; + border-image-slice: 1; + border-bottom: none; + border-left: none; + border-right: none; + width: -moz-available; + top: -0.2em; +} +.pokemon-card::after { + top: unset; + bottom: -0.2em; +} + +.pokemon-icon { + width: var(--card-img-max-height); + height: var(--card-img-max-height); +} +.pokemon-icon.minified { + width: var(--card-img-mini-max-height); + height: var(--card-img-mini-max-height); +} + +.pokemon-info { + margin-top: 1em; +} + +.pokemon-types { + display: flex; + justify-content: center; + margin-bottom: 1em; +} + +.pokemon-cards .pokemon-type-icon { + width: var(--card-icon-size); + height: var(--card-icon-size); + background-size: cover; + background-repeat: no-repeat; + background-position: left; + background-position-x: -0.07em; + border-radius: 50%; + margin: 0.1em 0.1em; +} +.pokemon-cards.minified .pokemon-type-icon { + width: var(--card-icon-mini-size); + height: var(--card-icon-mini-size); + background-position-x: -0.07em; + border: none; + border-radius: 0; + margin: 0; +} +.pokemon-card .pokemon-type-icon-wrapper { + border-radius: 50%; + margin: 0.2em 0.2em; + + -webkit-box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + -moz-box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + box-shadow: 0px 0px 0.2em 0.2em rgba(36, 36, 36, 0.9); + + overflow: hidden; +} +.pokemon-card .pokemon-type-effectiveness-wrapper { + padding-right: 0.7em !important; + padding-left: 0.7em !important; +} +.pokemon-card .pokemon-type-effectiveness-wrapper.disabled { + display: none; +} + +.pokemon-weaknesses, +.pokemon-resistances, +.pokemon-immunities { + min-width: var(--card-icon-size); + display: flex; + min-height: 5em; +} + +.pokemon-weaknesses { + background-color: #6a1f1f; + border: 0.2em solid red; + border-radius: 1em; +} + +.pokemon-resistances { + background-color: #216a1f; + border: 0.2em solid green; + border-radius: 1em; +} + +.pokemon-immunities { + background-color: #484848; + border: 0.2em solid white; + border-radius: 1em; +} + +.pokemon-weaknesses .pokemon-type-icon, +.pokemon-resistances .pokemon-type-icon, +.pokemon-immunities .pokemon-type-icon { + opacity: 1; +} + +.pokemon-card-text-wrapper { + padding: 0.3em 0.5em 0.5em 0.5em; +} + +.text-base { + display: flex; + font-family: var(--overlay-font); +} +.text-base span { + font-size: 2.5em; + line-height: 1; + text-shadow: var(--text-shadow-dark); + color: white; +} + +#enemies .text-base, +#allies .text-base { + font-family: var(--overlay-font); +} + +.arrow-button-wrapper { + display: block; +} +.arrow-button-wrapper button { + font-size: 2.5em; + background-color: rgb(30, 31, 34); + color: white; + line-height: 1; + border: 0.05em solid rgb(80, 80, 81); + border-right: none; +} +.arrow-button-wrapper.minified button { + font-size: 2.2em; +} +.arrow-button-wrapper.mobile button { + font-size: 3.5em; +} +.arrow-button-wrapper.mobile button { + display: grid; +} +.arrow-button-wrapper button:first-child { + border-top-left-radius: 0.4em; +} +.arrow-button-wrapper button:last-child { + border-bottom-left-radius: 0.4em; +} +.arrow-button-wrapper button:hover { + background-color: rgb(21, 22, 24); + cursor: pointer; +} +#enemies .arrow-button-wrapper button:hover { + border-color: var(--enemy-border-color); +} +#allies .arrow-button-wrapper button:hover { + border-color: var(--ally-border-color); +} +.arrow-button-wrapper button:active { + transform: scale(0.95); + background-color: rgb(26, 27, 29); +} + +.centered-flex { + display: flex; + justify-content: center; +} +.card-minified-info { + display: flex; + justify-content: flex-start; + margin-left: 8.5em; +} + +.tooltip .tooltiptext { + visibility: hidden; + text-align: center; + border-radius: 0.6em; + position: absolute; + font-family: var(--overlay-font); + background-color: white; + color: black; + text-shadow: none; + padding: 0.3em 0.4em 0.3em 0.4em; + z-index: 10; + + display: flex; + flex-direction: column; + align-items: flex-start; +} +.tooltip .tooltiptext span { + font-size: 2.5em; + color: black; + text-shadow: none; +} +.tooltip:hover .tooltiptext { + visibility: visible; +} +.tooltip:hover { + cursor: help; +} + +.image-overlay { + position: absolute; + left: -0.2em; + top: -4em; + width: 9.6em; + height: 9.6em; +} +.image-overlay.minified { + width: fit-content; + height: fit-content; +} + +.hidden-ability { + color: rgb(249 79 255) !important; +} + +#extension-status { + font-family: var(--overlay-font); + position: absolute; + width: 100%; + justify-content: center; + text-align: center; + color: blue; + background-color: #ffffff50; +} +#extension-status.statusbar-Top { + top: 0; + bottom: unset; +} +#extension-status.statusbar-Bottom { + top: unset; + bottom: 0; +} +#extension-status span { + text-shadow: var(--text-shadow-light); + padding: 0 0.5em; + font-size: 2em; + line-height: 1; +} +#extension-status .rd-status-text { + color: blue; +} +#extension-status .rd-status-session { + color: rgb(156, 2, 2); +} +#extension-status.sidebar-active { + background-color: #00000050; + display: block; + width: fit-content; + bottom: 0; + z-index: 1; + right: 0; +} +#extension-status.sidebar-active .rd-status-text { + display: none; +} +#extension-status.sidebar-active .rd-status-session { + text-shadow: none; + font-size: 1.5em; +} + +.pokemon-card .stat-cont { + padding: 0.2em 0.5em 0.2em 0.8em; +} +.pokemon-card .stat-cont .stat-p { + display: flex; + padding: 0 1.5em 0 0; + position: relative; +} +.pokemon-card .stat-cont .stat-p span { + display: inline-block; + padding: 0 0.1em 0 0; + color: white; + text-shadow: var(--text-shadow-dark); +} + +.pokemon-card .stat-cont .stat-c { + padding-right: 0.4em; + position: relative; + text-shadow: var(--text-shadow-dark); +} + +.pokemon-card .stat-cont .stat-icon { + padding: 0; + position: absolute; + top: -0.2em; + right: 0.2em; +} diff --git a/styles.css b/styles.css deleted file mode 100644 index 5c81c24..0000000 --- a/styles.css +++ /dev/null @@ -1,181 +0,0 @@ -:root { - --font-size: min(3vh, 2vw); - --icon-size: min(10vh, 6vw); -} - -.allies-team { - margin: 0 auto; - padding: 0 auto; - position: absolute; - top: 0; - right: 0; - z-index: 1; - display: flex; -} - -.enemy-team { - margin: 0 auto; - padding: 0 auto; - position: absolute; - top: 0; - z-index: 1; - display: flex; -} - -.pokemon-cards { - display: flex; - flex-wrap: nowrap; - overflow: hidden; -} - -.pokemon-card { - background-color: #333333b8; - border: 2px solid #555; - border-radius: 8px; - flex: 0 0 auto; - display:flex; - flex-direction: column; -} - -.pokemon-icon img { - width: min(var(--icon-size), 100px); - height: min(var(--icon-size), 100px); -} - -.pokemon-info { - margin-top: 10px; -} - -.pokemon-types { - display: flex; - justify-content: center; - margin-bottom: 10px; -} - -.type-icon { - --width: min(calc(var(--icon-size) / 2), 30px); - --height: min(calc(var(--icon-size) / 2), 30px); - width: var(--width); - height: var(--height); - background-size: cover; - background-repeat: no-repeat; - background-position: left; - background-position-x: min(-0.03em, -1.15px); - border-radius: 50%; - margin: 0 5px; -} - -.pokemon-weaknesses, -.pokemon-resistances, -.pokemon-immunities { - min-width: min(calc(var(--icon-size) / 2 + 1vh), 30px); - display: flex; - justify-content: center; -} - -.pokemon-weaknesses { - background-color: #6a1f1f; - border: 2px solid red; - border-radius: 10px; -} - -.pokemon-resistances { - background-color: #216a1f; - border: 2px solid green; - border-radius: 10px; -} - -.pokemon-immunities { - background-color: #484848; - border: 2px solid white; - border-radius: 10px; -} - -.pokemon-weaknesses .type-icon, -.pokemon-resistances .type-icon, -.pokemon-immunities .type-icon { - opacity: 1; -} - -.text-base { - display: flex; - font-family: 'emerald'; - font-size: min(var(--font-size), 18px); - text-shadow: 2px 0 #fff, -2px 0 #fff, 0 2px #fff, 0 -2px #fff, 1px 1px #fff, -1px -1px #fff, 1px -1px #fff, -1px 1px #fff; - color: black; -} - -.arrow-button-wrapper { - display: grid; -} -.arrow-button { - font-size: min(8vh, 3vw) -} - -.tooltip .tooltiptext { - visibility: hidden; - text-align: center; - border-radius: 6px; - padding: 5px 0; - font-size: var(--font-size); - position: absolute; - z-index: 1; - background-color: #fff; - max-width: 12vw; -} - -.tooltip:hover .tooltiptext { - visibility: visible; -} - -/* Slider settings */ -@media screen and (-webkit-min-device-pixel-ratio:0) { - input[type='range'] { - overflow: hidden; - width: min(20vh, 7.5vw); - -webkit-appearance: none; - } - - input[type='range']::-webkit-slider-runnable-track { - height: 1vh; - -webkit-appearance: none; - color: #13bba4; - margin-top: -1px; - } - - input[type='range']::-webkit-slider-thumb { - width: 1vh; - -webkit-appearance: none; - height: 1vh; - cursor: ew-resize; - box-shadow: -8vh 0 0 8vh #b3462c; - } - -} -/** FF*/ -input[type="range"]::-moz-range-progress { - background-color: #b3462c; -} -input[type="range"]::-moz-range-track { - background-color: #fff; -} -input[type="range"] { - background-color:transparent -} - -.slider-wrapper { - display: flex; -} - -.running-status { - position: absolute; - width: 100%; - justify-content: center; - text-align: center; - color: blue; - background-color: #ffffff50; -} - -.hidden-ability { - color: #f700ff; -} \ No newline at end of file diff --git a/webpack.config.cjs b/webpack.config.cjs new file mode 100644 index 0000000..9a93946 --- /dev/null +++ b/webpack.config.cjs @@ -0,0 +1,96 @@ +const path = require('path'); +const TerserPlugin = require('terser-webpack-plugin'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const ZipPlugin = require('zip-webpack-plugin'); + +module.exports = (env = {}) => { + const isDevelopment = env.NODE_ENV === 'development'; + + const createConfig = (browser, manifestFile) => { + const outputPath = path.resolve(__dirname, 'dist', browser); + + const optimization = { + minimize: !isDevelopment, + minimizer: [], + }; + + // Skip TerserPlugin in development mode + if (!isDevelopment) { + optimization.minimizer.push( + new TerserPlugin({ + terserOptions: { + format: { + comments: false, + }, + compress: { + pure_funcs: ['console.info', 'console.log'], + }, + }, + extractComments: false, // Prevents the creation of LICENSE.txt files + }) + ); + } + + const rules = [ + { + test: /\.js$/, + exclude: /node_modules/, + use: [], + }, + ]; + + // Skip Babel loader in development mode + if (!isDevelopment) { + rules[0].use.push({ + loader: 'babel-loader', + options: { + presets: ['@babel/preset-env'], + }, + }); + } + + return { + entry: { + background: './src/background.js', + content: './src/content/content.js', + inject: './src/inject.js', + }, + output: { + path: outputPath, + filename: '[name].js', + }, + module: { + rules, + }, + optimization, + plugins: [ + new CopyWebpackPlugin({ + patterns: [ + { from: `src/${manifestFile}`, to: 'manifest.json' }, + { from: 'src/options', to: 'options' }, + { from: 'src/styles.css', to: '' }, + { from: 'src/RogueDex.png', to: '' }, + { from: 'src/images', to: 'images' }, + { from: 'src/fonts', to: 'fonts' }, + { from: 'src/content', to: 'content' }, + { from: 'src/libs', to: 'libs' }, + { from: 'src/images', to: 'images' }, + ], + }), + new ZipPlugin({ + path: path.resolve(__dirname, 'dist'), + filename: `${browser}.zip`, + }), + ], + performance: { + hints: false, // Disables the size limit warnings + }, + mode: isDevelopment ? 'development' : 'production', + }; + }; + + return [ + createConfig('chrome', 'manifest.json'), + createConfig('firefox', 'manifest_firefox.json'), + ]; +};