commit 18318349c1a29aeb155118ad77cfb844ad2622d9 Author: Adam Date: Sun Jan 28 15:59:53 2024 -0600 cloning original project from github diff --git a/README-old.md b/README-old.md new file mode 100644 index 0000000..e90a18d --- /dev/null +++ b/README-old.md @@ -0,0 +1,333 @@ +# Journal to Canvas Slideshow + +- [WARNING](#warning) + +- [Journal to Canvas Slideshow](#journal-to-canvas-slideshow) + - [Feature Requests & The Future of Journal to Canvas Slideshow 01-16-2022](#feature-requests--the-future-of-journal-to-canvas-slideshow-01-16-2022) + - [WARNING](#warning) +- [About the Project](#about-the-project) + - [Built With](#built-with) +- [Installation and Getting Started](#installation-and-getting-started) +- [How to Use](#how-to-use) + - [Full Video Tutorial (Walkthrough of New Features Included)](#full-video-tutorial-walkthrough-of-new-features-included) + - [Written Tutorial with Gifs](#written-tutorial-with-gifs) + - [Videos in Journal](#videos-in-journal) + - [Display In Window and Module Settings](#display-in-window-and-module-settings) + - [Auto-Activate or Auto-Show Toggle](#auto-activate-or-auto-show-toggle) +- [Changelog](#changelog) + - [**v0.1.8 - v0.1.9** - 2022-01-16](#v018---v019---2022-01-16) + - [**v0.1.7** - 2021-08-26](#v017---2021-08-26) + - [**v0.1.6** - 2021-06-09](#v016---2021-06-09) + - [**v0.1.5** - 2021-05-23](#v015---2021-05-23) + - [Default Tile Control Tools](#default-tile-control-tools) + - [Tile Control Tools with Hide Tile Buttons Turned On](#tile-control-tools-with-hide-tile-buttons-turned-on) + - [**v0.1.4** - 2021-03-19](#v014---2021-03-19) + - [**v0.1.3** - 2021-01-22](#v013---2021-01-22) + - [**v0.1.2** - 2021-01-03](#v012---2021-01-03) + - [**v0.1.1** - 2020-12-28](#v011---2020-12-28) + - [**Added**](#added) + - [**Changes**](#changes) +- [Roadmap](#roadmap) +- [Code Explanation](#code-explanation) +- [Motivation](#motivation) +- [Credits](#credits) +- [Contact Me](#contact-me) + +Table of contents generated with markdown-toc + +## Feature Requests & The Future of Journal to Canvas Slideshow 01-16-2022 + +With my school workload, I unfortunately don't have time to implement any new features for this module going forward. I will try to fix any bugs that pop up, tweak any minor issues, and continue to keep it updated for major Foundry versions. If you want more/cooler/more flexible features, I would suggest checking out @DarKDinDoN 's amazing module Share Media, which was inspired by and improves upon this module :) +https://github.com/DarKDinDoN/share-media + +## WARNING + +If your image's source is from somewhere online, there's a chance there will be a CORS issue, and clicking on the image in the journal won't change the tile due to this error. Take a look at the dev console (F12 on Windows) and see if there's an error like this: https://i.imgur.com/SBHQPka.png + +If so, for now, try saving/downloading the image and placing it in your Foundry data folder somewhere, then in the journal entry, have the image's source link to that file path instead. I'll try to see if I can find a way around this. + +# About the Project + +This project brings functionality to images and videos in your Foundry journal entries and allows you to create a Display Scene. You can click on journal images and videos, which will change a tile in the Display Scene to match the image in the journal. + +!["Demonstration of module"](https://media4.giphy.com/media/nCBKGGweEGVYObw3ZB/giphy.gif) + +This is meant to make sharing art with your players while narrating or reading your notes a lot more seamless. It fits well with Theater of the Mind style play. + +## Built With + +- JavaScript +- JQuery +- HTML + +# Installation and Getting Started + +To install this module, go to the Configuration and Setup options in Foundry VTT, click on "Add-on Modules", then "Install Module". + +There will be a text box toward the bottom where you can paste this Manifest URL: + +https://raw.githubusercontent.com/EvanesceExotica/Journal-To-Canvas-Slideshow/master/module.json + +Then click "Install". + +# How to Use + +## Full Video Tutorial (Walkthrough of New Features Included) + +Please click on the image below for a full video tutorial with walk-throughs of new features included: + +[![Alt text](https://img.youtube.com/vi/t4NX55vs9gU/0.jpg)](https://www.youtube.com/watch?v=t4NX55vs9gU) + +(Click this link if the above doesn't work for some reason): + +https://youtu.be/t4NX55vs9gU + +--- + +## Written Tutorial with Gifs + +(**NOTE:** The instructions below are a bit outdated compared to the above video which includes explanations of new features and settings, but they still generally apply. ) + +1. Open your game in FoundryVTT and navigate to the "Scenes" tab. + +2. At the bottom of this tab, there will be a button that reads "Create or Show Display Scene". +3. Click that button. A new scene will be generated named "Display", with a single tile with a dark background. + +!["Demonstration of Create or Show Display Scene button"](https://media4.giphy.com/media/PHOiojIZhG3hyg4uIm/giphy.gif) + +4. If you click on this button again while the Display scene already exists, it will activate the Display scene instead of creating a new one. ~~(**Warning**: _Do not_ rename this scene, as the script searches for a scene named "Display" specifically. I may try to change this in the future so it keeps track of and searches for the scene's ID instead.)~~ (No longer true in v0.1.4 -- see Change Log below for details) + +5. You can change the scene's background image and add extra tiles for decoration, ~~but + make sure that the very first tile on the canvas is the tile you wish to display your images.~~ (No longer necessary in version 0.1.4 -- see Change Log below for details) +6. Open up a journal entry with images (**Note**: This should work with Image Mode journal entries too). You should notice the images highlight and gain a shadow when you hover over them. +7. Clicking on them will cause the tile in the Display scene to resize/reposition and change to match the image you clicked on. +8. Have fun! + +--- + +## Videos in Journal + +This project does support .webm and .mp4 files, however inserting a video into a journal entry requires a different approach than using an image. + +1. Check out this page: https://www.w3schools.com/html/html5_video.asp and scroll down a bit to the part that says "Example". + +2. Open up your journal entry, click the edit button, then click the button along the top that looks like this "< >" to access the entry's source code. + +!["Clicking on source code button"](https://i.imgur.com/dUgvvBS.png?1) + +3. You can copy and paste the code example shown on the above w3schools site into the source code, and change the file path in quotations (where it says , change the 'movie.mp4' part to be the file path of your .webm or .mp4 video and the type to the matching type "mp4" or "webm"). + +!["Placing video element in source code"](https://i.imgur.com/uXnoRxU.png=800x600) + +4. You can then click on the video, and it will change the Display tile. + +!["Clicking on vid to change tile"](https://media0.giphy.com/media/rF4mjVrm5L6Y4MrmSx/giphy.gif) + +--- + +## Display In Window and Module Settings + +The module now includes a feature to display your journal images in a window rather than in a dedicated scene. This will allow you to click through and present your 'slideshow' to your players even while they're on another scene, like a battlemap. + +!["Display in Window"](https://media.giphy.com/media/2ubtah0ZPWcWtpzyh6/giphy.gif) + +(Note: If above gif doesn't display, please click this link to see it: https://media.giphy.com/media/2ubtah0ZPWcWtpzyh6/source.gif) + +In order to use this new feature + +1. navigate to the "Journal" tab and click on the button that says "Create or Show Display Entry". A journal will be created named "Display Journal". + +(_Note_: You can click this button again after the Display Journal is created to show the Display Journal to all of your players.) + +2. Navigate to "Journal to Canvas Slideshow"'s settings in the "Module Settings" tab of the "Configure Game Settings" window. + +3. You will see a setting called "Display Location", and you can switch it from "Scene" to "Window". + +!["Module settings"](https://i.imgur.com/djMriuR.jpg) + +4. This will allow clicked-on journal images to be shown in the Display Journal rather than in the Display scene, as demonstrated in the above gif. + +( _Note_: For right now, to 'clear' the Display Journal, (while it's selected as the Display Location in the module settings), you can click on the same "ClearDisplay" button under the Tiles section of the scene controls as you would use to clear the tile in the Display scene. This will set the image to a blank/transparent image. I intend to add a button to the actual window later on. ) + +### Auto-Activate or Auto-Show Toggle + +A toggleable option is also included in the module settings to automatically activate the "Display" scene OR automatically show the "Display Journal" window to players when you click on a journal image, depending on which option you have selected for the Display Location setting. + +!["Module settings1"](https://i.imgur.com/k3CwDBa.jpg) + +(_Note_: A notification appears each time you click a journal image while having this setting turned on in conjunction with the "Window" option for the Display Location setting. This is because it uses the same functionality as the "Show Players" button at the top of a journal in image mode. I'll have to figure out how to change that if possible.) + +# Changelog + +## **v0.1.8 - v0.1.9** - 2022-01-16 + +**CHANGED** + +- Updated for Foundry version 9. Check in "releases" for the version still compatible with version 8. + +## **v0.1.7** - 2021-08-26 + +**CHANGED** + +- Integrated features from pull requests, such as item images now being able to be clicked on and displayed. (Thanks, @DarKDinDoN !) +- Added setting to hide or change how "Toggle Display Location" button in journal header displays. + +## **v0.1.6** - 2021-06-09 + +Updated module to work with Foundry v8.6 + +## **v0.1.5** - 2021-05-23 + +**ADDED** + +**Major** + +- NEW: Ability to _right click_ on actor sheet character images to display them the same as journal images. + +- NEW: Ability to display Journal-to-Canvas-Slideshow tools within a dialog rather than as tile control tools. + +See the settings for **Use Actor Sheet Images** and **Hide Tile Buttons** in the updated module settings below. +!["New Settings"](https://i.imgur.com/AfHLPSG.png) + +### Default Tile Control Tools + +The default tile control tools with the Hide Tile Buttons setting disabled. + +A new button is there called "Switch Display Location" that will display a dialog that allows you to switch display locations without needing to go into the module's settings. + +!["Switch Display Location Button"](https://i.imgur.com/3XLHTku.png) + +!["Switch Display Location Dialog"](https://i.imgur.com/CBSidW0.png) + +**Note**: Journal entries now have a button in the header that allows you to switch the display location as well. + +**Note**: You can switch away from the tile control tools and then back again to "refresh" if you enable or disable the Hide Tile Buttons setting. + +--- + +### Tile Control Tools with Hide Tile Buttons Turned On + +With the Hide Tile Buttons setting enabled, all Journal-to-Canvas-Slideshow buttons will not be displayed except for the "Clear Display" button, and a new button that says "Show Slideshow Config". + +To show the other functions, click on the button in the tile control tools that says "Show Slideshow Config". + +!["Hide Tile Buttons Setting Turned On"](https://i.imgur.com/6a7oxpt.png) + +The following dialog will appear with buttons with all the functionality, such as creating Display and Bounding Tiles, Setting a URL image, and switching between display locations. + +!["Hide Tile Buttons Dialog"](https://i.imgur.com/u5DWfMc.png) + +--- + +**CHANGED**: + +- Many features now work with VIEWED scene rather than ACTIVE scene, such as the bounding tiles. + +--- + +## **v0.1.4** - 2021-03-19 + +**ADDED** + +**Major**: + +- NEW: Bounding Tiles implemented by @Occidio +- NEW: Display Tiles that along with Bounding Tiles can be added to _any scene_. +- NEW: Display images via copy-pasting URL feature implemented by @p4535992 +- NEW: Display in Window feature alternative implemented by @DarKDinDoN +- NEW: Extra settings to accomodate the above new features -- please check the settings menu and reselect your prefered settings. + +**Changes**: + +**Major:** + +- Special "Display Tiles" now created via button in Tile controls menu. Flagged by script, so no longer have to be very first tile in scene. +- **Warning**: Please replace regular tile in pre-made Display Scenes with new Display Tile, else the script will not detect them. + +## **v0.1.3** - 2021-01-22 + +**ADDED** + +**Major**: + +- NEW: Added option to display journal images in a window rather than display scene + +- NEW: Module settings + +## **v0.1.2** - 2021-01-03 + +**Major:** + +- Fixed an incompatability issue with the Call of Cthulhu 7e (CoC7) system. + +## **v0.1.1** - 2020-12-28 + +### **Added** + +**Major:** + +- Added "Clear Display" button in Tiles scene control buttons. Will set 'slideshow' tile to a transparent image. + +**Minor:** + +- More visual effects when hovering over and clicking images in journal, for more user feedback +- Changed cursor to pointer on hover of journal images + +### **Changes** + +- Clicking on image in journal no longer activates the 'Display' scene if a different scene is active. Plan to add functionality later to toggle this behavior. + +(Red arrow pointing at new 'Clear Display' button') +!["Location of clear button"](https://i.imgur.com/aPtU9QL.jpg) + +!["Showing off updates"](https://media2.giphy.com/media/sIKIPBhN3c5vLPVxGu/giphy.gif) + +# Roadmap + +- I next intend to add a way to more easily toggle between the various different settings (Display in Window vs Display in Scene, etc.) without needing to go all the way to the settings menu. + +- I may possibily implement a way to have multiple Display Tiles in a single scene, but I will need to think of the best way to implement this. + +# Code Explanation + +The real 'magic' basically surrounds a single tile. Each time I click on an image in the journal entry, the tile's image source is updated with the source changed to that of the image in the journal that I clicked on. + +The script basically goes through the images in a journal entry when it renders, adds a 'clickable-image' class to them, and then looks through the elements with that class and adds an event handler when they're clicked. + +This event handler handles changing the tile's source in a specific scene called "Display" to that of the image in the journal. + +# Motivation + +I love being able to share art with my players. Though I know some groups prefer complete Theater of the Mind using only imagination, images help me and my group with immersion, improv, and with 'pseudo-TOTM' battles when I don't want to use a battlemap. + +The motivation behind this project is to have a journal entry with many images, that you can click through to show your players art for characters, locations, monsters, etc. while not having to miss a beat if you also need to narrate. + +Years ago I tried to do this using Google Slides, but it was clunky and a pain in the butt. Then another VTT (GM Forge) came along that had this feature and I absolutely loved it, but the VTT stopped being supported. + +I then tried to replicate the feature in other VTTs but nothing quite worked the way I wanted. + +Upon getting Foundry VTT, I finally learned Javascript, with some help from some StackOverflow posts and reading over the source code for the Image-Drop, Sound-Link and Journal-Links modules helped me figure out how to implement this feature on my own. + +# Credits + +The following users for their awesome contributions to this module: + +@p4535992 https://github.com/p4535992 + +@Occidio https://github.com/Occidio + +@DarKDinDoN https://github.com/DarKDinDoN + +@zeel01 for the implementation of their MultiMediaPopout class from their module Show art: https://github.com/zeel01/TokenHUDArtButton + +GMForge -- this feature was originally built into [GMForge](https://store.steampowered.com/app/842250/GM_Forge__Virtual_Tabletop/) which I used while it was still supported. I adored this feature and wanted to bring it to Foundry VTT, as I could never quite find another solution that scratched this itch and satisfied me in the same way. + +My figuring out how to do this in FoundryVTT is thanks largely to studying the source code of the modules [Drag Upload](https://github.com/cswendrowski/FoundryVTT-Drag-Upload) by _cswendrowski_, [Image-Drop](https://gitlab.com/mesfoliesludiques/foundryvtt-image-drop) by _U~man_, [Journal-Links](https://github.com/Sigafoos/journal-links) by _Sigafoos_ and [Sound-Link](https://github.com/superseva/sound-link) by _superseva_. + +Thanks to Joe Neeves whose tabletop art from Gumroad: https://gumroad.com/limonium provided the decorative tiles. + +# Contact Me + +_Discord_: Eva#3782 + +_Email_: EvanesceExotica@gmail.com diff --git a/README.md b/README.md new file mode 100644 index 0000000..6a8fe6a --- /dev/null +++ b/README.md @@ -0,0 +1,116 @@ +- [JTCS - Art Gallery](#jtcs---art-gallery) + - [Features, Walkthrough and Changelog](#features-walkthrough-and-changelog) + - [Walkthrough/Tutorial](#walkthroughtutorial) + - [Major Feature Additions and Improvements](#major-feature-additions-and-improvements) + - [**IMPROVED**](#improved) + - [**NEW**](#new) + - [Previous Versions Documentation](#previous-versions-documentation) + - [Changelog/Release Notes](#changelogrelease-notes) + - [Recommended Modules](#recommended-modules) + - [Contributors & Thanks](#contributors--thanks) + - [Code Contributors](#code-contributors) + - [Visual FX/UI/Assets Contributions](#visual-fxuiassets-contributions) + +# JTCS - Art Gallery + +Journal to Canvas Slideshow (Now renamed to "JTCS - Art Gallery") has received a major overhaul and several big feature updates. + +Now with support for Foundry v10! + +In addition to big updates to the core features, and major QOL improvements, the biggest new feature is called the "Art Gallery" which allows you to display multiple different images on multiple different "Art Tiles" in the same scene, bound by "Frame Tiles" that keep the Art Tiles scaled within a certain size. + +Here is a video demonstration: + + + +Note: While the wooden scene background and overlay art is included as part of the module in a compendium pack, the tabletop "trinket" assets seen in above video are NOT included as part of the module. They are by [Joe Neeves (Limonium) on Gumroad](https://limonium.gumroad.com/?recommended_by=library). + +## Features, Walkthrough and Changelog + +### Walkthrough/Tutorial + +A detailed walkthrough of the new features can be found here: [Features and Walkthrough](features-and-walkthrough.md) + +### Major Feature Additions and Improvements + +#### **IMPROVED** + +- Added support for Foundry VTT v10, with backwards compability for v9 + +- Improved image-share controls in actor, item, and journal sheets + + - Various Display methods can be easily accessed and activated by hovering over an image and clicking on one of these controls, rather than having to change the method via settings + - the Original "click on an image to display it on the canvas" functionality remains intact. + + Image Controls Demo + +#### **NEW** + +- Gallery Tiles + + - "Gallery Tiles" feature introduced, allowing the creation of "Art Tiles" and "Frame Tiles" (which are an overhauled and much more robust version of the old 'Display Tiles' and 'Bounding Tiles' feature) + - Gallery Tiles can be created, linked, configured, and given unique names in a new configuration application called the "Scene Gallery Config" + + Scene Gallery Config App - Light Mode + +- Settings and Customization + + - JTCS Art Gallery Settings application that can be launched from multiple locations and includes several customization options + - Canvas tiles highlight with colored overlays whenever you hover a connected UI item, to ensure you can easily find them. + + - overlay colors are customizable + + - Gallery Tile color customization options in the JTCS Art Gallery Settings App + + Tile Colors Demo + + - Demonstration of how the colors translate to affect the UI elements and canvas tile overlays + Color Demo Template + + - Color customization of elements UI in JTCS Art Gallery apps, including a default light and dark theme. + Background Color Change Demo + + Scene Gallery Config App - Dark Mode + Scene Gallery Config App - Light Mode + +- Compendiums + - Compendium pack of macros with featuring utilities to make moving and scaling tiles easier + - Compendium pack of premade scenes displaying demo setups of Gallery tiles, including a scene meant to act as your default "Display Scene" + - Compendium pack of Journal Entries including a scene meant to act as your default "Display Journal" + +### Previous Versions Documentation + +For access to the older/previous version documentation, please see [Old Readme](README-old.md) + +### Changelog/Release Notes + +Go to [Release Notes](release-notes.md) to view the full changelog/release notes. + +## Recommended Modules + +- [Tile Sort] by [theripper93](https://github.com/theripper93) - highly recommended for taking the pain out of layering tiles +- [Quickscale](https://foundryvtt.com/packages/quickscale) by [unsoluble](https://foundryvtt.com/community/unsoluble) - will make scaling and rotating tiles easier +- [FX Master](https://foundryvtt.com/packages/fxmaster) by [ghost](https://foundryvtt.com/community/saluu) - for cool visual effects on your Art Gallery scenes +- [Token Magic FX](https://foundryvtt.com/packages/tokenmagic) by [SecretFire](https://foundryvtt.com/community/galaktor) - for adding add filters and effects to tiles + +## Contributors & Thanks + +Thanks to everyone, both users and contributors, for being patient with me as this project has evolved. This module was one of my first forays into the realm of web development, and I've learned so much since its inception, both through schooling and the painstaking but fulfilling process of gradually trying to make my modules better and better. + +This update was a huge undertaking for me, but aso an amazing learning experience, and I hope to continue adding improvements as needed. + +I hope everyone enjoys! + +### Code Contributors + +Thank you to everyone who contributed, added and suggested features, and helped me out while I was still a beginner, including: + +- - @Occidio +- - @MaximeTKT +- - @P4535992 + +### Visual FX/UI/Assets Contributions + +- FoundryVTT UI Theme shown in demo vids and images is [Polished UI](https://foundryvtt.com/packages/polished-ui) by [erizocosmico](https://foundryvtt.com/community/erizocosmico) +- Visual FX - [FX Master](https://foundryvtt.com/packages/fxmaster) +- Tabletop Trinket Assets by Joe Neeves (Limonium) on Gumroad - [Limonium's Gumroad Library](https://limonium.gumroad.com/?recommended_by=library) diff --git a/assets/AssetAttributions.md b/assets/AssetAttributions.md new file mode 100644 index 0000000..e3a5f8d --- /dev/null +++ b/assets/AssetAttributions.md @@ -0,0 +1,20 @@ +# Attributions: + +## Arrow Icons: + +### Url + +https://game-icons.net/tags/arrow.html + +### File Paths + +"assets\contract.svg" +"assets\multi-directions.svg" + +### Authors: + +Lorc, Delapouite & contributors + +### License + +CC BY 3.0 diff --git a/assets/Bounding_Tile.webp b/assets/Bounding_Tile.webp new file mode 100644 index 0000000..3d67209 Binary files /dev/null and b/assets/Bounding_Tile.webp differ diff --git a/assets/Character-Frames.png b/assets/Character-Frames.png new file mode 100644 index 0000000..5696aae Binary files /dev/null and b/assets/Character-Frames.png differ diff --git a/assets/DarkBackground.webp b/assets/DarkBackground.webp new file mode 100644 index 0000000..210bfbf Binary files /dev/null and b/assets/DarkBackground.webp differ diff --git a/assets/FramesBG.png b/assets/FramesBG.png new file mode 100644 index 0000000..3705bb7 Binary files /dev/null and b/assets/FramesBG.png differ diff --git a/assets/HD_transparent_picture.webp b/assets/HD_transparent_picture.webp new file mode 100644 index 0000000..3d67209 Binary files /dev/null and b/assets/HD_transparent_picture.webp differ diff --git a/assets/contract.svg b/assets/contract.svg new file mode 100644 index 0000000..171aaa4 --- /dev/null +++ b/assets/contract.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/fvtt-JournalEntry-art.json b/assets/fvtt-JournalEntry-art.json new file mode 100644 index 0000000..5734361 --- /dev/null +++ b/assets/fvtt-JournalEntry-art.json @@ -0,0 +1,18 @@ +{ + "name": "Art", + "content": "

 

\n

Tristique et Egestas Quis Ipsum

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ornare arcu dui vivamus arcu. Iaculis nunc sed augue lacus viverra vitae congue. Et tortor at risus viverra adipiscing at in tellus. 

\n

\"\"

\n

 

\n

\n

 

\n

Massa eget egestas purus viverra accumsan. Luctus accumsan tortor posuere ac ut consequat semper viverra nam. Aliquam etiam erat velit scelerisque in dictum non consectetur a. Cras tincidunt lobortis feugiat vivamus at augue eget arcu. Vitae suscipit tellus mauris a. At elementum eu facilisis sed odio morbi quis commodo odio. Consectetur purus ut faucibus pulvinar elementum integer enim neque.

\n

 

\n

Aliquet Lectus Proin

\n

 

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Imperdiet sed euismod nisi porta lorem. Pharetra pharetra massa massa ultricies. Gravida neque convallis a cras. Tristique risus nec feugiat in fermentum. Adipiscing commodo elit at imperdiet dui.

\n

 

\n

Nibh sit Amet

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nibh sit amet commodo nulla facilisi nullam vehicula ipsum. Felis eget nunc lobortis mattis. Quam lacus suspendisse faucibus interdum posuere lorem. Adipiscing commodo elit at imperdiet dui. Semper quis lectus nulla at. Leo in vitae turpis massa sed elementum.

\n

 

\n

\n

Feugiat in Fermentum

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Donec adipiscing tristique risus nec feugiat in. Ultrices vitae auctor eu augue ut lectus arcu bibendum at. Tempus imperdiet nulla malesuada pellentesque elit eget. Habitasse platea dictumst quisque sagittis purus. Facilisis leo vel fringilla est.

\n

\n

\n

 

\n

Tempus imperdiet

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At quis risus sed vulputate. Vestibulum lectus mauris ultrices eros in cursus turpis massa tincidunt. Dictum at tempor commodo ullamcorper a lacus vestibulum sed. Aenean vel elit scelerisque mauris. Aliquam sem fringilla ut morbi tincidunt augue interdum.

\n


\n

\n

 

\n

Vestibulum lectus mauris

\n

\n

 

\n

Sagittis nisl rhoncus mattis rhoncus. ie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed. At volutpat diam ut venenatis tellus in. Consequat interdum varius sit amet mattis vulputate enim nulla. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere. Vitae aliquet nec ullamcorper sit amet risus nullam. Elit duis tristique sollicitudin nibh sit amet. Lorem mollis aliquam ut porttitor leo.

\n

 

\n

Eu volutpat odio facilisis mauris sit amet massa vitae. Massa sed elementum tempus egestas sed sed risus pretium quam. Id neque aliquam vestibulum morbi blandit. Sed id semper risus in hendrerit gravida. Ut consequat semper viverra nam libero. A lacus vestibulum sed arcu non. Quam elementum pulvinar etiam non quam lacus. Neque laoreet suspendisse interdum consectetur. Platea dictumst quisque sagittis purus sit amet volutpat consequat. Euismod in pellentesque massa placerat.

\n

 

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus at augue eget arcu dictum. Non nisi est sit amet facilisis magna etiam. Nec dui nunc mattis enim ut tellus elementum sagittis vitae. Nunc mattis enim ut tellus elementum sagittis vitae. Dui id ornare arcu odio.

\n

 

\n

", + "flags": { + "core": { + "sourceId": "JournalEntry.wwfHk5WNE5tkayrB" + }, + "world": { + "displayLocation": "anyScene" + }, + "exportSource": { + "world": "moduletestworld", + "system": "cyphersystem", + "coreVersion": "9.269", + "systemVersion": "1.32.3" + } + } +} \ No newline at end of file diff --git a/assets/fvtt-JournalEntry-video-demo.json b/assets/fvtt-JournalEntry-video-demo.json new file mode 100644 index 0000000..9bead8e --- /dev/null +++ b/assets/fvtt-JournalEntry-video-demo.json @@ -0,0 +1,15 @@ +{ + "name": "Video Demo", + "content": "

\n

", + "flags": { + "core": { + "sourceId": "JournalEntry.k3tNlbsTTVCKviiK" + }, + "exportSource": { + "world": "moduletestworld", + "system": "cyphersystem", + "coreVersion": "9.269", + "systemVersion": "1.32.3" + } + } +} \ No newline at end of file diff --git a/assets/multi-directions.svg b/assets/multi-directions.svg new file mode 100644 index 0000000..24899b8 --- /dev/null +++ b/assets/multi-directions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/debug.log b/debug.log new file mode 100644 index 0000000..ef8f9a7 --- /dev/null +++ b/debug.log @@ -0,0 +1,9 @@ +[0826/121332.698:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/121332.918:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/122428.170:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/122428.356:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/122447.230:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/122447.395:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/122748.143:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/123336.898:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) +[0826/123337.074:ERROR:registration_protocol_win.cc(102)] CreateFile: The system cannot find the file specified. (0x2) diff --git a/demo-images/Videos/StarrySparkles.mp4 b/demo-images/Videos/StarrySparkles.mp4 new file mode 100644 index 0000000..dccba68 Binary files /dev/null and b/demo-images/Videos/StarrySparkles.mp4 differ diff --git a/demo-images/pd19-20049_1.webp b/demo-images/pd19-20049_1.webp new file mode 100644 index 0000000..a9c5253 Binary files /dev/null and b/demo-images/pd19-20049_1.webp differ diff --git a/demo-images/pd19-20051_1.webp b/demo-images/pd19-20051_1.webp new file mode 100644 index 0000000..9c4ed6b Binary files /dev/null and b/demo-images/pd19-20051_1.webp differ diff --git a/demo-images/pd19-20054_1.webp b/demo-images/pd19-20054_1.webp new file mode 100644 index 0000000..928c448 Binary files /dev/null and b/demo-images/pd19-20054_1.webp differ diff --git a/demo-images/pd19-20091_1.webp b/demo-images/pd19-20091_1.webp new file mode 100644 index 0000000..0a2ab5f Binary files /dev/null and b/demo-images/pd19-20091_1.webp differ diff --git a/demo-images/pd20-120069-ake.webp b/demo-images/pd20-120069-ake.webp new file mode 100644 index 0000000..2e49cf7 Binary files /dev/null and b/demo-images/pd20-120069-ake.webp differ diff --git a/demo-images/pd20-360084-num.webp b/demo-images/pd20-360084-num.webp new file mode 100644 index 0000000..d32736c Binary files /dev/null and b/demo-images/pd20-360084-num.webp differ diff --git a/demo-images/pd20-360102-num.webp b/demo-images/pd20-360102-num.webp new file mode 100644 index 0000000..d1088f1 Binary files /dev/null and b/demo-images/pd20-360102-num.webp differ diff --git a/demo-images/pd21-021999-436-jj.webp b/demo-images/pd21-021999-436-jj.webp new file mode 100644 index 0000000..3a5f8db Binary files /dev/null and b/demo-images/pd21-021999-436-jj.webp differ diff --git a/demo-images/pd21batch3-rp-p-1935-907-jj_2_1.webp b/demo-images/pd21batch3-rp-p-1935-907-jj_2_1.webp new file mode 100644 index 0000000..568cbad Binary files /dev/null and b/demo-images/pd21batch3-rp-p-1935-907-jj_2_1.webp differ diff --git a/demo-images/pd22-06-0151-nam-02.webp b/demo-images/pd22-06-0151-nam-02.webp new file mode 100644 index 0000000..8abf236 Binary files /dev/null and b/demo-images/pd22-06-0151-nam-02.webp differ diff --git a/demo-images/pd22-06-0186-nam-jite-02.webp b/demo-images/pd22-06-0186-nam-jite-02.webp new file mode 100644 index 0000000..24ab034 Binary files /dev/null and b/demo-images/pd22-06-0186-nam-jite-02.webp differ diff --git a/demo-images/pd23-011-jj.webp b/demo-images/pd23-011-jj.webp new file mode 100644 index 0000000..e5c5598 Binary files /dev/null and b/demo-images/pd23-011-jj.webp differ diff --git a/demo-images/pd42-63143347-rob.webp b/demo-images/pd42-63143347-rob.webp new file mode 100644 index 0000000..64d63d2 Binary files /dev/null and b/demo-images/pd42-63143347-rob.webp differ diff --git a/demo-images/pd42-63143348-rob.webp b/demo-images/pd42-63143348-rob.webp new file mode 100644 index 0000000..271fa22 Binary files /dev/null and b/demo-images/pd42-63143348-rob.webp differ diff --git a/demo-images/pd42-63143351-rob.webp b/demo-images/pd42-63143351-rob.webp new file mode 100644 index 0000000..4c75e12 Binary files /dev/null and b/demo-images/pd42-63143351-rob.webp differ diff --git a/features-and-walkthrough.md b/features-and-walkthrough.md new file mode 100644 index 0000000..95cc367 --- /dev/null +++ b/features-and-walkthrough.md @@ -0,0 +1,308 @@ +# JTCS Art Gallery + +### Table of Contents + +- [JTCS Art Gallery](#jtcs-art-gallery) + - [Table of Contents](#table-of-contents) +- [Display Methods](#display-methods) + - [Window Popouts, Art Journal and Art Scene - (Upgraded Features)](#window-popouts-art-journal-and-art-scene---upgraded-features) + - [URL Image Sharing](#url-image-sharing) + - [URL Sharing Limitations (IMPORTANT)](#url-sharing-limitations-important) + - [URL Image Sharing (Cont.)](#url-image-sharing-cont) + - [The Art Gallery - (New Features!)](#the-art-gallery---new-features) + - [Art Gallery Config](#art-gallery-config) + - [Default Art Tiles](#default-art-tiles) + - [Art Tiles and Frame Tiles](#art-tiles-and-frame-tiles) + - [Frame Tiles vs Decorative Tiles](#frame-tiles-vs-decorative-tiles) + - [Art and Frame Tiles (Cont.)](#art-and-frame-tiles-cont) + - [Gallery Tile Creation](#gallery-tile-creation) + - [Linking Preexisting Tiles](#linking-preexisting-tiles) + - [URL Image Sharing on Specific Art Tiles](#url-image-sharing-on-specific-art-tiles) +- [Utilities, Settings and Customization](#utilities-settings-and-customization) + - [Color Theme and Color Customization](#color-theme-and-color-customization) + - [Custom Colors](#custom-colors) + - [Default Dark and Light Theme](#default-dark-and-light-theme) +- [Tips and Utilities](#tips-and-utilities) + - [Fading Overlapping Elements](#fading-overlapping-elements) + - [Fading Tiles](#fading-tiles) + - [Fading Journals](#fading-journals) + - [Fading the Scene Gallery Config App](#fading-the-scene-gallery-config-app) + +(**Note**: Some of the footage below is a teeny bit outdated, with small visual and functional bugs having being fixed since they were recorded; If your interface looks a little bit different than in the videos below, it is due to those updates; but all major functionality should be the same ) + +# Display Methods + +- Hovering over an image on an actor, item, or journal entry sheet will display controls that represent different methods by which you can display that image. + +- these controls can be toggled on and off by type or on each individual sheet (see "[Utilities, Settings and Customization](#utilities-settings-and-customization)" section for more information) + +- You can also click* on the image itself, which will send it to whichever tile is selected as "Default" in that scene (see "[Default Art Tiles](#default-art-tiles)" section for more information) + +## Important Note about Clicking Foundry V10* + +In FoundryVTT Version 10, left-clicking on an image in a Journal Sheet now has the core functionality of displaying an image in a window popout. + +Instead of overriding this functionality, I instead allowed it to be possible to right-click on Journal Sheet images instead. This right-click functionality was already implemented for images on Item and Actor sheets, as left-clicking on those images causes the file-picker to open. + +While both right-clicking and left-clicking in Journal Sheets will send the image to the "Default" tile as stated above, right-clicking will avoid opening the Image Popout. + + + +> display on default tile demo + +## Window Popouts, Art Journal and Art Scene - (Upgraded Features) + +Two of the options in the controls allow you to display images in a window popout, or a dedicated Journal Entry + +(Note: The window popout option will render a new "popout" window each time you click it, allowing multiple windows for multiple different images, while the Journal Entry option will simply change the image in that specific journal, showing one image at a time, and providing more of a "slideshow" effect. + + + +> journal entry popover demo + +You can also have a dedicated "Art Scene" that images are sent to. Images displayed through this method will automatically display on the "Default Art Tile" in that Art Scene (see "[Default Art Tiles](#default-art-tiles)" section for more info). + + + +> art scene demo + +## URL Image Sharing + +You can also share images via URLs through the Scene Gallery Config App. + +Clicking on the first button at the top will open up a Dialog window. Here you can paste your image URL, and select whichever display method you would like. + + + +--- + +### URL Sharing Limitations (IMPORTANT) + +Do be aware that for web security reasons, not all image URLs will work. +Some sites do not allow resources like images to be shared "externally". + +See these Wikipedia articles for more information: + +- [Same-Origin Policy | Wikipedia](https://en.wikipedia.org/wiki/Same-origin_policy) +- [Cross-Origin Resource Sharing | Wikipedia](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) + +--- + +### URL Image Sharing (Cont.) + +The display methods in the URL Image Sharing Dialog work pretty much identically those available when hovering over a Journal Entry, Actor or Item Sheet. + +The below options are included in both: + +- Window - will render a new "popout" window each time you click it, allowing multiple windows for multiple different images +- Art Journal - will simply change the image in your Art Journal, showing one image at a time, and providing a "slideshow" effect. +- Art Scene - will automatically display on the "Default Art Tile" in that Art Scene (see "[Default Art Tiles](#default-art-tiles)" section for more info). + + + +Note: A fourth option to Share URLs on specific tiles will be explained below in the Art Gallery section. + +## The Art Gallery - (New Features!) + +Clicking the top button on the Sheet Image controls will display a button list of all of the tiles in the scene, and clicking on one of those buttons will display the specific image on that tile + + + +> art gallery display demo + +### Art Gallery Config + +Open the Art Gallery Config to see all of the Gallery Tiles a scene. + +You can open it by clicking on a button in the "Tiles" controls, or via utility buttons on any Journal Entry, Actor, or Item sheet. + +Contextual instructions about each type of tile will show to the right + +Click on the floating i button to toggle contextual instructions on and off. + + + +> config highlight demo + +The Gallery Tiles shown in the "Art Gallery Config" automatically change depending on which scene you're viewing. + +Upon switching a scene, the Config will update to show that particular scene's Art Gallery tiles. + + + +> change config based on scene + +### Default Art Tiles + +Select a "Default Art Tile" for each scene in the Art Gallery config with Ctrl/Cmd + Click. +(The "Default" tile will automatically be set to the first "linked" Art Tile in a scene ) + + + +> default Art Tile demo + +Clicking on a sheet image itself, rather than any of its controls, will automatically send it to the "Default Art Tile" in the current scene + + + +> display on default tile demo + +Note: If you haven't manually selected a Default Art Tile, the first Art Tile in a scene that is linked to a tile on the canvas will be chosen as the Default Art Tile. + +If there is no Art Tile that fits these conditions, you will receive a notification that there are no Default Art Tiles in the scene. + +### Art Tiles and Frame Tiles + +- A frame tile acts like an "Frame" for the Art Tile. +- The "Frame" Tile will contain the Art Tile within it, making sure it gets no larger than the frame, but maintaining the image's original dimensions/aspect ratio. +- While an "Art Tile" can only have one "Frame Tile", a "Frame" tile can have more than one "Art Tile" linked to it, which can be useful for many reasons. + +--- + +### Frame Tiles vs Decorative Tiles + +Frame Tiles are completely transparent by default, and are used specifically for sizing and positioning Art Tiles, not really meant for displaying anything themselves. If you want the visual of a "Frame" (like a picture frame, a door, a window, etc.) overlayed on top of your Art Tiles, like is demonstrated in the included "Premade Gallery Scene", you could either use 'decorative tiles', tiles on the canvas that are not linked to an Art or Frame tile, and position them manually, or use two Art Tiles bound to the same Frame Tile, with one on top to represent the actual visual "Frame", and another below to represent the "Art". + +Here's an example of how you could layer the various Gallery tile types with decorative tiles and/or scene foreground/background images + +tile layer demo diagram + +--- + +### Art and Frame Tiles (Cont.) + +By default after an Art Tile is created, or if you select "Use Canvas as Frame", the Art Tile will treat the scene canvas's boundaries as its frame, getting no bigger than that. + + + +> Art frame tile bounding demo + +You can change an Art Tile's "Frame" by selecting a new Frame in the dropdown on the ArtTile. +Hovering over an Art Tile in the Art Gallery Config will also highlight the Frame Tile it was linked to + + + +> frame and art tile link demo + +### Gallery Tile Creation + +When creating a new scene or viewing one without Art Gallery tiles, the Art Gallery Config will be blank. + +You can create a new Gallery tile of either type by clicking on the "Create new Frame Tile" or "New Art Tile" buttons at the bottom of each column in the Art Gallery Config. + +By default, these tiles will be "Unlinked", meaning they aren't connected to a 'physical' tile on the canvas. Unlinked tiles will show a warning on them indicating that they aren't linked. + +To link an Gallery tile to a 'physical' tile on the Canvas, there are two methods. + +One is to click the "Plus" button on an unlinked tile, which will create a new "blank" tile linked. + + + +> create new tile demo + +A new Art Tile will be created with a black gradient as its image, while a new frame tile will have a completely transparent image (however you can still highlight both by hovering over them in the Art Gallery Config) + +You can change the 'default' image for both Gallery tile types in the settings, as this also affects what image the Gallery tile 'resets' to if you 'clear' it (more on this functionality to be explained later) + +### Linking Preexisting Tiles + +If you have pre-existing canvas tiles you wish to turn _into_ Gallery Tiles, you can do that too. + +Upon creating a new Gallery Tile in the Art Gallery Config, one of the buttons that will appear has a "link" icon. + +Clicking on that button display a list of all the "Unlinked" tile objects in your current scene. + +You can hover over each item in the list to highlight the specific tile on the canvas, and then click to select which one you would like to "link" to the Gallery Tile. + + + +This works the same for both Art and Frame Tiles. + +### URL Image Sharing on Specific Art Tiles + +- Clicking the icon highlighted below on a Gallery Tile item in the Scene Gallery Tile Config will bring up a small textbox. + +share url on tile icon + +share url on tile textbox + +- Here you can paste your Image URL and press Enter/Return (or click outside of the text box), and the image pointed to by the URL will be shared on the specific Art Tile. + + + +# Utilities, Settings and Customization + +You can configure and customize various settings for this module. + +The JTCS Art Gallery settings application can either be accessed as usual through the "Module Settings" tab in Foundry's default settings, or by clicking on this Gear icon in the Art Gallery Config app or on a sheet that has the controls toggled on. + +launching settings app + +JTCS Settings App Window +> Here is what the JTCS Art Gallery Settings App looks like + +## Color Theme and Color Customization + +### Custom Colors + +You can change the colors of various UI elements in the JTCS Art Gallery. + + + +(Selecting a new color, and clicking "Apply" to see how it appears. ) + +The "Accent Color" property affects the colors of buttons, controls and inputs, including the color of the controls in Actor, Journal, and Item Sheets. + +color change controls + +The "Background Color" property affects the background color of the "JTCS Art Gallery Settings" application and the "Scene Gallery Config" application. + +color change controls + +The "Tile Indicator Colors" affect the colors that represent the different types of Gallery Tiles, both in the UI elements that represent them, and in the color they are highlighted on the canvas when hovering over the items in the UI. + +frame art default color + +ColorDemoTemplateFlat + +### Default Dark and Light Theme + +There are two default themes included, one Dark and one Light. + +You can switch between them by clicking one of the buttons in the JTCS Art Gallery Settings app, but do be aware that doing so will overwrite any custom colors you have chosen. + + + +# Tips and Utilities + +## Fading Overlapping Elements + +There are various ways to fade parts of the UI or tiles on the screen to better see the Highlighted Gallery Tiles on the Canvas. + +### Fading Tiles + +Clicking the button with two overlapping squares on a Gallery Tile item in the Scene Gallery Config App will make all tiles in the scene except the one whose button you clicked upon fade out/become partially translucent, allowing you to better see the highlighted area that represents each Gallery Tile if you have lots of tiles overlaid on top of each other. + + + +### Fading Journals + +You can fade out the UI of the Journal Entry, Actor, and Item Sheets by clicking the Icon that looks like an Eye in the controls at the Sheets' bottom. + + + +The transparency/fade can be toggled off by clicking the eye button once again. + +fade toggle demo + +(Note: (QOL/UX Improvement) This feature could be improved so that the Fade Toggle Button itself is fully opaque and visible regardless of if the fade feature is toggled on or off) + +### Fading the Scene Gallery Config App + +This goes similarly for the Scene Gallery Config App. + + + +(Note: The value dictating how transparent the document Sheets and the Scene Gallery Config App become when the fade function is toggled on can be changed in the JTCS Art Gallery Settings. ) + +fade section in config diff --git a/journal-to-canvas-slideshow.code-workspace b/journal-to-canvas-slideshow.code-workspace new file mode 100644 index 0000000..8da8035 --- /dev/null +++ b/journal-to-canvas-slideshow.code-workspace @@ -0,0 +1,20 @@ +{ + "folders": [ + { + "name": "journal-to-canvas-slideshow", + "path": "." + } + ], + "settings": { + "gotoSymbolStack.currentStackPosition": 0, + "gotoSymbolStack.filePositionInfo": [], + "gotoSymbolStack.maxStackPosition": 0, + "cSpell.words": ["JTCS", "PIXI", "Popouts"], + "workbench.tree.renderIndentGuides": "always", + "todoist.projectId": 2297684184, + "dimmer.enabled": false + }, + "extensions": { + "recommendations": ["riazxrazor.html-to-jsx"] + } +} diff --git a/module.json b/module.json new file mode 100644 index 0000000..d0da61a --- /dev/null +++ b/module.json @@ -0,0 +1,83 @@ +{ + "id": "journal-to-canvas-slideshow", + "title": "JTCS - Art Gallery", + "description": "Show art from journal, actor, or item sheets to your players in a variety of ways, now including displaying multiple images simultaneously in tile-based 'Art Gallery'. (Previously named 'Journal-to-Canvas-Slideshow')", + "authors": [ + { + "name": "Eva", + "email": "evanesceexotica@gmail.com", + "discord": "Eva#3782" + } + ], + "socket": true, + "version": "0.2.4", + "compatibility": { + "minimum": "9", + "verified": "10", + "maximum": "11" + }, + "relationships": [ + null + ], + "packs": [ + { + "name": "jtcs-premade-scenes", + "label": "JTCS Premade Display Scenes", + "path": "packs/jtcs-premade-display-scenes", + "type": "Scene", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "jtcs-premade-journals", + "label": "JTCS Premade Journal Entries", + "path": "packs/jtcs-premade-journal-entries", + "type": "JournalEntry", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + }, + { + "name": "jtcs-utility-macros", + "label": "JTCS Utility Macros", + "path": "packs/jtcs-utility-macros", + "type": "Macro", + "ownership": { + "PLAYER": "OBSERVER", + "ASSISTANT": "OWNER" + } + } + ], + "esmodules": [ + "scripts/shim.js", + "scripts/ClickImageInJournal.js", + "scripts/classes/MultiMediaPopout.js", + "scripts/classes/SlideshowConfig.test.js", + "scripts/SlideshowConfig.js", + "scripts/init.js", + "scripts/DragDropIntoJournal.js", + "scripts/classes/ArtTileManager.js", + "scripts/classes/CanvasIndicators.js", + "scripts/debug-mode.js", + "scripts/SheetImageApp.js", + "scripts/classes/HelperFunctions.js", + "scripts/classes/ImageDisplayManager.js", + "scripts/classes/JTCSSettingsApplication.js", + "scripts/data/JTCS-Actions.js", + "scripts/data/templates.js", + "scripts/data/SlideshowConfigActions.js", + "scripts/classes/PopoverGenerator.js", + "scripts/hooks.js", + "scripts/data/ModuleManager.js", + "scripts/classes/SheetImage.test.js" + ], + "styles": [ + "styles/styles.css" + ], + "url": "https://github.com/EvanesceExotica/Journal-To-Canvas-Slideshow", + "manifest": "https://raw.githubusercontent.com/EvanesceExotica/Journal-To-Canvas-Slideshow/master/module.json", + "download": "https://github.com/EvanesceExotica/Journal-To-Canvas-Slideshow/archive/refs/heads/master.zip" +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a63740a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,6011 @@ +{ + "name": "journal-to-canvas-slideshow", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "devDependencies": { + "jest": "^29.0.1" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", + "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", + "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.13", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.13", + "@babel/types": "^7.18.13", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", + "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.13", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", + "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", + "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.13", + "@babel/types": "^7.18.13", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", + "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.0.1.tgz", + "integrity": "sha512-SxLvSKf9gk4Rvt3p2KRQWVQ3sVj7S37rjlCHwp2+xNcRO/X+Uw0idbkfOtciUpjghHIxyggqcrrKhThQ+vClLQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.0.1.tgz", + "integrity": "sha512-EcFrXkYh8I1GYHRH9V4TU7jr4P6ckaPqGo/z4AIJjHDZxicjYgWB6fx1xFb5bhEM87eUjCF4FAY5t+RamLWQmA==", + "dev": true, + "dependencies": { + "@jest/console": "^29.0.1", + "@jest/reporters": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.0.0", + "jest-config": "^29.0.1", + "jest-haste-map": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-resolve-dependencies": "^29.0.1", + "jest-runner": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "jest-watcher": "^29.0.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.1.tgz", + "integrity": "sha512-iLcFfoq2K6DAB+Mc+2VNLzZVmHdwQFeSqvoM/X8SMON6s/+yEi1iuRX3snx/JfwSnvmiMXjSr0lktxNxOcqXYA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "jest-mock": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.1.tgz", + "integrity": "sha512-qKB3q52XDV8VUEiqKKLgLrJx7puQ8sYVqIDlul6n7SIXWS97DOK3KqbR2rDDaMtmenRHqEUl2fI+aFzx0oSemA==", + "dev": true, + "dependencies": { + "expect": "^29.0.1", + "jest-snapshot": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.1.tgz", + "integrity": "sha512-Tw5kUUOKmXGQDmQ9TSgTraFFS7HMC1HG/B7y0AN2G2UzjdAXz9BzK2rmNpCSDl7g7y0Gf/VLBm//blonvhtOTQ==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.0.1.tgz", + "integrity": "sha512-XZ+kAhLChVQ+KJNa5034p7O1Mz3vtWrelxDcMoxhZkgqmWDaEQAW9qJeutaeCfPvwaEwKYVyKDYfWpcyT8RiMw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.0.1", + "jest-mock": "^29.0.1", + "jest-util": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.0.1.tgz", + "integrity": "sha512-BtZWrVrKRKNUt7T1H2S8Mz31PN7ItROCmH+V5pn10hJDUfjOCTIUwb0WtLZzm0f1tJ3Uvx+5lVZrF/VTKqNaFg==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.1", + "@jest/expect": "^29.0.1", + "@jest/types": "^29.0.1", + "jest-mock": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.0.1.tgz", + "integrity": "sha512-dM3L8JmYYOsdeXUUVZClQy67Tz/v1sMo9h4AQv2U+716VLHV0zdA6Hh4FQNAHMhYw/95dbZbPX8Q+TRR7Rw+wA==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "jest-worker": "^29.0.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.0.0.tgz", + "integrity": "sha512-nOr+0EM8GiHf34mq2GcJyz/gYFyLQ2INDhAylrZJ9mMWoW21mLBfZa0BUVPPMxVYrLjeiRe2Z7kWXOGnS0TFhQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.0.1.tgz", + "integrity": "sha512-XCA4whh/igxjBaR/Hg8qwFd/uTsauoD7QAdAYUjV2CSGx0+iunhjoCRRWTwqjQrETRqOJABx6kNfw0+C0vMSgQ==", + "dev": true, + "dependencies": { + "@jest/console": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.0.1.tgz", + "integrity": "sha512-3GhSBMCRcWXGluP2Dw7CLP6mNke/t+EcftF5YjzhX1BJmqcatMbtZVwjuCfZy0TCME1GevXy3qTyV5PLpwIFKQ==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.0.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.1.tgz", + "integrity": "sha512-6UxXtqrPScFdDhoip8ys60dQAIYppQinyR87n9nlasR/ZnFfJohKToqzM29KK4gb9gHRv5oDFChdqZKE0SIhsg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.0.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.1.tgz", + "integrity": "sha512-ft01rxzVsbh9qZPJ6EFgAIj3PT9FCRfBF9Xljo2/33VDOUjLZr0ZJ2oKANqh9S/K0/GERCsHDAQlBwj7RxA+9g==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.28", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", + "integrity": "sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", + "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/node": { + "version": "18.7.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", + "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/babel-jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.0.1.tgz", + "integrity": "sha512-wyI9r8tqwsZEMWiIaYjdUJ6ztZIO4DMWpGq7laW34wR71WtRS+D/iBEtXOP5W2aSYCVUQMsypRl/xiJYZznnTg==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.0.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.0.0.tgz", + "integrity": "sha512-B9oaXrlxXHFWeWqhDPg03iqQd2UN/mg/VdZOsLaqAVBkztru3ctTryAI4zisxLEEgmcUnLTKewqx0gGifoXD3A==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.0.0.tgz", + "integrity": "sha512-B5Ke47Xcs8rDF3p1korT3LoilpADCwbG93ALqtvqu6Xpf4d8alKkrCBTExbNzdHJcIuEPpfYvEaFFRGee2kUgQ==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.0.0", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001384", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001384.tgz", + "integrity": "sha512-BBWt57kqWbc0GYZXb47wTXpmAgqr5LSibPzNjk/AWMdmJMQhLqOl3c/Kd4OAU/tu4NLfYkMx8Tlq3RVBkOBolQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", + "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.233", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.233.tgz", + "integrity": "sha512-ejwIKXTg1wqbmkcRJh9Ur3hFGHFDZDw1POzdsVrB2WZjgRuRMHIQQKNpe64N/qh3ZtH2otEoRoS+s6arAAuAAw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.1.tgz", + "integrity": "sha512-yQgemsjLU+1S8t2A7pXT3Sn/v5/37LY8J+tocWtKEA0iEYYc6gfKbbJJX2fxHZmd7K9WpdbQqXUpmYkq1aewYg==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.0.1.tgz", + "integrity": "sha512-liHkwzaW6iwQyhRBFj0A4ZYKcsQ7ers1s62CCT95fPeNzoxT/vQRWwjTT4e7jpSCwrvPP2t1VESuy7GrXcr2ug==", + "dev": true, + "dependencies": { + "@jest/core": "^29.0.1", + "@jest/types": "^29.0.1", + "import-local": "^3.0.2", + "jest-cli": "^29.0.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.0.0.tgz", + "integrity": "sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.0.1.tgz", + "integrity": "sha512-I5J4LyK3qPo8EnqPmxsMAVR+2SFx7JOaZsbqW9xQmk4UDmTCD92EQgS162Ey3Jq6CfpKJKFDhzhG3QqiE0fRbw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.1", + "@jest/expect": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.0.1", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.0.1.tgz", + "integrity": "sha512-XozBHtoJCS6mnjCxNESyGm47Y4xSWzNlBJj4tix9nGrG6m068B83lrTWKtjYAenYSfOqyYVpQCkyqUp35IT+qA==", + "dev": true, + "dependencies": { + "@jest/core": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.0.1.tgz", + "integrity": "sha512-3duIx5ucEPIsUOESDTuasMfqHonD0oZRjqHycIMHSC4JwbvHDjAWNKN/NiM0ZxHXjAYrMTLt2QxSQ+IqlbYE5A==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.0.1", + "@jest/types": "^29.0.1", + "babel-jest": "^29.0.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.0.1", + "jest-environment-node": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-runner": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.1.tgz", + "integrity": "sha512-l8PYeq2VhcdxG9tl5cU78ClAlg/N7RtVSp0v3MlXURR0Y99i6eFnegmasOandyTmO6uEdo20+FByAjBFEO9nuw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.0.0", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.0.0.tgz", + "integrity": "sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.0.1.tgz", + "integrity": "sha512-UmCZYU9LPvRfSDoCrKJqrCNmgTYGGb3Ga6IVsnnVjedBTRRR9GJMca7UmDKRrJ1s+U632xrVtiRD27BxaG1aaQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "jest-util": "^29.0.1", + "pretty-format": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.0.1.tgz", + "integrity": "sha512-PcIRBrEBFAPBqkbL53ZpEvTptcAnOW6/lDfqBfACMm3vkVT0N7DcfkH/hqNSbDmSxzGr0FtJI6Ej3TPhveWCMA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.1", + "@jest/fake-timers": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "jest-mock": "^29.0.1", + "jest-util": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", + "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.1.tgz", + "integrity": "sha512-gcKOAydafpGoSBvcj/mGCfhOKO8fRLkAeee1KXGdcJ1Pb9O2nnOl4I8bQSIID2MaZeMHtLLgNboukh/pUGkBtg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.1", + "jest-worker": "^29.0.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.0.1.tgz", + "integrity": "sha512-5tISHJphB+sCmKXtVHJGQGltj7ksrLLb9vkuNWwFR86Of1tfzjskvrrrZU1gSzEfWC+qXIn4tuh8noKHYGMIPA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.1.tgz", + "integrity": "sha512-/e6UbCDmprRQFnl7+uBKqn4G22c/OmwriE5KCMVqxhElKCQUDcFnq5XM9iJeKtzy4DUjxT27y9VHmKPD8BQPaw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.0.1", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.1.tgz", + "integrity": "sha512-wRMAQt3HrLpxSubdnzOo68QoTfQ+NLXFzU0Heb18ZUzO2S9GgaXNEdQ4rpd0fI9dq2NXkpCk1IUWSqzYKji64A==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.0.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.0.1.tgz", + "integrity": "sha512-i1yTceg2GKJwUNZFjIzrH7Y74fN1SKJWxQX/Vu3LT4TiJerFARH5l+4URNyapZ+DNpchHYrGOP2deVbn3ma8JA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "@types/node": "*" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.0.0.tgz", + "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.0.1.tgz", + "integrity": "sha512-dwb5Z0lLZbptlBtPExqsHfdDamXeiRLv4vdkfPrN84vBwLSWHWcXjlM2JXD/KLSQfljBcXbzI/PDvUJuTQ84Nw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.0.1.tgz", + "integrity": "sha512-fUGcYlSc1NzNz+tsHDjjG0rclw6blJcFZsLEsezxm/n54bAm9HFvJxgBuCV1CJQoPtIx6AfR+tXkR9lpWJs2LQ==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.0.0", + "jest-snapshot": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.0.1.tgz", + "integrity": "sha512-XeFfPmHtO7HyZyD1uJeO4Oqa8PyTbDHzS1YdGrvsFXk/A5eXinbqA5a42VUEqvsKQgNnKTl5NJD0UtDWg7cQ2A==", + "dev": true, + "dependencies": { + "@jest/console": "^29.0.1", + "@jest/environment": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.0.0", + "jest-environment-node": "^29.0.1", + "jest-haste-map": "^29.0.1", + "jest-leak-detector": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-resolve": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-util": "^29.0.1", + "jest-watcher": "^29.0.1", + "jest-worker": "^29.0.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.0.1.tgz", + "integrity": "sha512-yDgz5OE0Rm44PUAfTqwA6cDFnTYnVcYbRpPECsokSASQ0I5RXpnKPVr2g0CYZWKzbsXqqtmM7TIk7CAutZJ7gQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.0.1", + "@jest/fake-timers": "^29.0.1", + "@jest/globals": "^29.0.1", + "@jest/source-map": "^29.0.0", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-mock": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.0.1.tgz", + "integrity": "sha512-OuYGp+lsh7RhB3DDX36z/pzrGm2F740e5ERG9PQpJyDknCRtWdhaehBQyMqDnsQdKkvC2zOcetcxskiHjO7e8Q==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.0.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-haste-map": "^29.0.1", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.0.1", + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.1.tgz", + "integrity": "sha512-GIWkgNfkeA9d84rORDHPGGTFBrRD13A38QVSKE0bVrGSnoR1KDn8Kqz+0yI5kezMgbT/7zrWaruWP1Kbghlb2A==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.0.1.tgz", + "integrity": "sha512-mS4q7F738YXZFWBPqE+NjHU/gEOs7IBIFQ8i9zq5EO691cLrUbLhFq4larf8/lNcmauRO71tn/+DTW2y+MrLow==", + "dev": true, + "dependencies": { + "@jest/types": "^29.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "leven": "^3.1.0", + "pretty-format": "^29.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.0.1.tgz", + "integrity": "sha512-0LBWDL3sZ+vyHRYxjqm2irhfwhUXHonjLSbd0oDeGq44U1e1uUh3icWNXYF8HO/UEnOoa6+OJDncLUXP2Hdg9A==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^29.0.1", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.1.tgz", + "integrity": "sha512-+B/2/8WW7goit7qVezG9vnI1QP3dlmuzi2W0zxazAQQ8dcDIA63dDn6j4pjOGBARha/ZevcwYQtNIzCySbS7fQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.1.tgz", + "integrity": "sha512-iTHy3QZMzuL484mSTYbQIM1AHhEQsH8mXWS2/vd2yFBYnG3EBqGiMONo28PlPgrW7P/8s/1ISv+y7WH306l8cw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@babel/code-frame": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dev": true, + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.13.tgz", + "integrity": "sha512-5yUzC5LqyTFp2HLmDoxGQelcdYgSpP9xsnMWBphAscOdFrHSAVbLNzWiy32sVNDqJRDiJK6klfDnAgu6PAGSHw==", + "dev": true + }, + "@babel/core": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.13.tgz", + "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==", + "dev": true, + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.13", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.13", + "@babel/types": "^7.18.13", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "@babel/generator": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.13.tgz", + "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==", + "dev": true, + "requires": { + "@babel/types": "^7.18.13", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-compilation-targets": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", + "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "dev": true + }, + "@babel/helper-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz", + "integrity": "sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", + "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz", + "integrity": "sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w==", + "dev": true + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "dev": true + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "dev": true + }, + "@babel/helpers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", + "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "dev": true, + "requires": { + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.18.13.tgz", + "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==", + "dev": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz", + "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.18.13.tgz", + "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.13", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.18.13", + "@babel/types": "^7.18.13", + "debug": "^4.1.0", + "globals": "^11.1.0" + } + }, + "@babel/types": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.18.13.tgz", + "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==", + "dev": true, + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jest/console": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.0.1.tgz", + "integrity": "sha512-SxLvSKf9gk4Rvt3p2KRQWVQ3sVj7S37rjlCHwp2+xNcRO/X+Uw0idbkfOtciUpjghHIxyggqcrrKhThQ+vClLQ==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.0.1.tgz", + "integrity": "sha512-EcFrXkYh8I1GYHRH9V4TU7jr4P6ckaPqGo/z4AIJjHDZxicjYgWB6fx1xFb5bhEM87eUjCF4FAY5t+RamLWQmA==", + "dev": true, + "requires": { + "@jest/console": "^29.0.1", + "@jest/reporters": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.0.0", + "jest-config": "^29.0.1", + "jest-haste-map": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-resolve-dependencies": "^29.0.1", + "jest-runner": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "jest-watcher": "^29.0.1", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.0.1.tgz", + "integrity": "sha512-iLcFfoq2K6DAB+Mc+2VNLzZVmHdwQFeSqvoM/X8SMON6s/+yEi1iuRX3snx/JfwSnvmiMXjSr0lktxNxOcqXYA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "jest-mock": "^29.0.1" + } + }, + "@jest/expect": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.0.1.tgz", + "integrity": "sha512-qKB3q52XDV8VUEiqKKLgLrJx7puQ8sYVqIDlul6n7SIXWS97DOK3KqbR2rDDaMtmenRHqEUl2fI+aFzx0oSemA==", + "dev": true, + "requires": { + "expect": "^29.0.1", + "jest-snapshot": "^29.0.1" + } + }, + "@jest/expect-utils": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.0.1.tgz", + "integrity": "sha512-Tw5kUUOKmXGQDmQ9TSgTraFFS7HMC1HG/B7y0AN2G2UzjdAXz9BzK2rmNpCSDl7g7y0Gf/VLBm//blonvhtOTQ==", + "dev": true, + "requires": { + "jest-get-type": "^29.0.0" + } + }, + "@jest/fake-timers": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.0.1.tgz", + "integrity": "sha512-XZ+kAhLChVQ+KJNa5034p7O1Mz3vtWrelxDcMoxhZkgqmWDaEQAW9qJeutaeCfPvwaEwKYVyKDYfWpcyT8RiMw==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^29.0.1", + "jest-mock": "^29.0.1", + "jest-util": "^29.0.1" + } + }, + "@jest/globals": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.0.1.tgz", + "integrity": "sha512-BtZWrVrKRKNUt7T1H2S8Mz31PN7ItROCmH+V5pn10hJDUfjOCTIUwb0WtLZzm0f1tJ3Uvx+5lVZrF/VTKqNaFg==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.1", + "@jest/expect": "^29.0.1", + "@jest/types": "^29.0.1", + "jest-mock": "^29.0.1" + } + }, + "@jest/reporters": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.0.1.tgz", + "integrity": "sha512-dM3L8JmYYOsdeXUUVZClQy67Tz/v1sMo9h4AQv2U+716VLHV0zdA6Hh4FQNAHMhYw/95dbZbPX8Q+TRR7Rw+wA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@jridgewell/trace-mapping": "^0.3.15", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^5.1.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "jest-worker": "^29.0.1", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + } + }, + "@jest/schemas": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.0.0.tgz", + "integrity": "sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.0.0.tgz", + "integrity": "sha512-nOr+0EM8GiHf34mq2GcJyz/gYFyLQ2INDhAylrZJ9mMWoW21mLBfZa0BUVPPMxVYrLjeiRe2Z7kWXOGnS0TFhQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.15", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.0.1.tgz", + "integrity": "sha512-XCA4whh/igxjBaR/Hg8qwFd/uTsauoD7QAdAYUjV2CSGx0+iunhjoCRRWTwqjQrETRqOJABx6kNfw0+C0vMSgQ==", + "dev": true, + "requires": { + "@jest/console": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.0.1.tgz", + "integrity": "sha512-3GhSBMCRcWXGluP2Dw7CLP6mNke/t+EcftF5YjzhX1BJmqcatMbtZVwjuCfZy0TCME1GevXy3qTyV5PLpwIFKQ==", + "dev": true, + "requires": { + "@jest/test-result": "^29.0.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.0.1.tgz", + "integrity": "sha512-6UxXtqrPScFdDhoip8ys60dQAIYppQinyR87n9nlasR/ZnFfJohKToqzM29KK4gb9gHRv5oDFChdqZKE0SIhsg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.0.1", + "@jridgewell/trace-mapping": "^0.3.15", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.1", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + } + }, + "@jest/types": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.0.1.tgz", + "integrity": "sha512-ft01rxzVsbh9qZPJ6EFgAIj3PT9FCRfBF9Xljo2/33VDOUjLZr0ZJ2oKANqh9S/K0/GERCsHDAQlBwj7RxA+9g==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.15", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz", + "integrity": "sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@sinclair/typebox": { + "version": "0.24.28", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", + "integrity": "sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow==", + "dev": true + }, + "@sinonjs/commons": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", + "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@types/babel__core": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.19.tgz", + "integrity": "sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.18.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.0.tgz", + "integrity": "sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/graceful-fs": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.5.tgz", + "integrity": "sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/node": { + "version": "18.7.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.7.13.tgz", + "integrity": "sha512-46yIhxSe5xEaJZXWdIBP7GU4HDTG8/eo0qd9atdiL+lFpA03y8KS+lkTN834TWJj5767GbWv4n/P6efyTFt1Dw==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "@types/yargs": { + "version": "17.0.11", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.11.tgz", + "integrity": "sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "babel-jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.0.1.tgz", + "integrity": "sha512-wyI9r8tqwsZEMWiIaYjdUJ6ztZIO4DMWpGq7laW34wR71WtRS+D/iBEtXOP5W2aSYCVUQMsypRl/xiJYZznnTg==", + "dev": true, + "requires": { + "@jest/transform": "^29.0.1", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + } + }, + "babel-plugin-jest-hoist": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.0.0.tgz", + "integrity": "sha512-B9oaXrlxXHFWeWqhDPg03iqQd2UN/mg/VdZOsLaqAVBkztru3ctTryAI4zisxLEEgmcUnLTKewqx0gGifoXD3A==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.0.0.tgz", + "integrity": "sha512-B5Ke47Xcs8rDF3p1korT3LoilpADCwbG93ALqtvqu6Xpf4d8alKkrCBTExbNzdHJcIuEPpfYvEaFFRGee2kUgQ==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^29.0.0", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001384", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001384.tgz", + "integrity": "sha512-BBWt57kqWbc0GYZXb47wTXpmAgqr5LSibPzNjk/AWMdmJMQhLqOl3c/Kd4OAU/tu4NLfYkMx8Tlq3RVBkOBolQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true + }, + "ci-info": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.3.2.tgz", + "integrity": "sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz", + "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true + }, + "collect-v8-coverage": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", + "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "convert-source-map": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.8.0.tgz", + "integrity": "sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true + }, + "diff-sequences": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.0.0.tgz", + "integrity": "sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA==", + "dev": true + }, + "electron-to-chromium": { + "version": "1.4.233", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.233.tgz", + "integrity": "sha512-ejwIKXTg1wqbmkcRJh9Ur3hFGHFDZDw1POzdsVrB2WZjgRuRMHIQQKNpe64N/qh3ZtH2otEoRoS+s6arAAuAAw==", + "dev": true + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.0.1.tgz", + "integrity": "sha512-yQgemsjLU+1S8t2A7pXT3Sn/v5/37LY8J+tocWtKEA0iEYYc6gfKbbJJX2fxHZmd7K9WpdbQqXUpmYkq1aewYg==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fb-watchman": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", + "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "dev": true, + "requires": { + "bser": "2.1.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true + }, + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "dev": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^3.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.0.1.tgz", + "integrity": "sha512-liHkwzaW6iwQyhRBFj0A4ZYKcsQ7ers1s62CCT95fPeNzoxT/vQRWwjTT4e7jpSCwrvPP2t1VESuy7GrXcr2ug==", + "dev": true, + "requires": { + "@jest/core": "^29.0.1", + "@jest/types": "^29.0.1", + "import-local": "^3.0.2", + "jest-cli": "^29.0.1" + } + }, + "jest-changed-files": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.0.0.tgz", + "integrity": "sha512-28/iDMDrUpGoCitTURuDqUzWQoWmOmOKOFST1mi2lwh62X4BFf6khgH3uSuo1e49X/UDjuApAj3w0wLOex4VPQ==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + } + }, + "jest-circus": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.0.1.tgz", + "integrity": "sha512-I5J4LyK3qPo8EnqPmxsMAVR+2SFx7JOaZsbqW9xQmk4UDmTCD92EQgS162Ey3Jq6CfpKJKFDhzhG3QqiE0fRbw==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.1", + "@jest/expect": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.0.1", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "p-limit": "^3.1.0", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-cli": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.0.1.tgz", + "integrity": "sha512-XozBHtoJCS6mnjCxNESyGm47Y4xSWzNlBJj4tix9nGrG6m068B83lrTWKtjYAenYSfOqyYVpQCkyqUp35IT+qA==", + "dev": true, + "requires": { + "@jest/core": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.0.1.tgz", + "integrity": "sha512-3duIx5ucEPIsUOESDTuasMfqHonD0oZRjqHycIMHSC4JwbvHDjAWNKN/NiM0ZxHXjAYrMTLt2QxSQ+IqlbYE5A==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.0.1", + "@jest/types": "^29.0.1", + "babel-jest": "^29.0.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.0.1", + "jest-environment-node": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-runner": "^29.0.1", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + } + }, + "jest-diff": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.0.1.tgz", + "integrity": "sha512-l8PYeq2VhcdxG9tl5cU78ClAlg/N7RtVSp0v3MlXURR0Y99i6eFnegmasOandyTmO6uEdo20+FByAjBFEO9nuw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.0.0", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + } + }, + "jest-docblock": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.0.0.tgz", + "integrity": "sha512-s5Kpra/kLzbqu9dEjov30kj1n4tfu3e7Pl8v+f8jOkeWNqM6Ds8jRaJfZow3ducoQUrf2Z4rs2N5S3zXnb83gw==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.0.1.tgz", + "integrity": "sha512-UmCZYU9LPvRfSDoCrKJqrCNmgTYGGb3Ga6IVsnnVjedBTRRR9GJMca7UmDKRrJ1s+U632xrVtiRD27BxaG1aaQ==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "jest-util": "^29.0.1", + "pretty-format": "^29.0.1" + } + }, + "jest-environment-node": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.0.1.tgz", + "integrity": "sha512-PcIRBrEBFAPBqkbL53ZpEvTptcAnOW6/lDfqBfACMm3vkVT0N7DcfkH/hqNSbDmSxzGr0FtJI6Ej3TPhveWCMA==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.1", + "@jest/fake-timers": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "jest-mock": "^29.0.1", + "jest-util": "^29.0.1" + } + }, + "jest-get-type": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.0.0.tgz", + "integrity": "sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.0.1.tgz", + "integrity": "sha512-gcKOAydafpGoSBvcj/mGCfhOKO8fRLkAeee1KXGdcJ1Pb9O2nnOl4I8bQSIID2MaZeMHtLLgNboukh/pUGkBtg==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.0.0", + "jest-util": "^29.0.1", + "jest-worker": "^29.0.1", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-leak-detector": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.0.1.tgz", + "integrity": "sha512-5tISHJphB+sCmKXtVHJGQGltj7ksrLLb9vkuNWwFR86Of1tfzjskvrrrZU1gSzEfWC+qXIn4tuh8noKHYGMIPA==", + "dev": true, + "requires": { + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + } + }, + "jest-matcher-utils": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.0.1.tgz", + "integrity": "sha512-/e6UbCDmprRQFnl7+uBKqn4G22c/OmwriE5KCMVqxhElKCQUDcFnq5XM9iJeKtzy4DUjxT27y9VHmKPD8BQPaw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.0.1", + "jest-get-type": "^29.0.0", + "pretty-format": "^29.0.1" + } + }, + "jest-message-util": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.0.1.tgz", + "integrity": "sha512-wRMAQt3HrLpxSubdnzOo68QoTfQ+NLXFzU0Heb18ZUzO2S9GgaXNEdQ4rpd0fI9dq2NXkpCk1IUWSqzYKji64A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.0.1", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.0.1", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.0.1.tgz", + "integrity": "sha512-i1yTceg2GKJwUNZFjIzrH7Y74fN1SKJWxQX/Vu3LT4TiJerFARH5l+4URNyapZ+DNpchHYrGOP2deVbn3ma8JA==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "@types/node": "*" + } + }, + "jest-pnp-resolver": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", + "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", + "dev": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.0.0.tgz", + "integrity": "sha512-BV7VW7Sy0fInHWN93MMPtlClweYv2qrSCwfeFWmpribGZtQPWNvRSq9XOVgOEjU1iBGRKXUZil0o2AH7Iy9Lug==", + "dev": true + }, + "jest-resolve": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.0.1.tgz", + "integrity": "sha512-dwb5Z0lLZbptlBtPExqsHfdDamXeiRLv4vdkfPrN84vBwLSWHWcXjlM2JXD/KLSQfljBcXbzI/PDvUJuTQ84Nw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.0.1", + "jest-validate": "^29.0.1", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + } + }, + "jest-resolve-dependencies": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.0.1.tgz", + "integrity": "sha512-fUGcYlSc1NzNz+tsHDjjG0rclw6blJcFZsLEsezxm/n54bAm9HFvJxgBuCV1CJQoPtIx6AfR+tXkR9lpWJs2LQ==", + "dev": true, + "requires": { + "jest-regex-util": "^29.0.0", + "jest-snapshot": "^29.0.1" + } + }, + "jest-runner": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.0.1.tgz", + "integrity": "sha512-XeFfPmHtO7HyZyD1uJeO4Oqa8PyTbDHzS1YdGrvsFXk/A5eXinbqA5a42VUEqvsKQgNnKTl5NJD0UtDWg7cQ2A==", + "dev": true, + "requires": { + "@jest/console": "^29.0.1", + "@jest/environment": "^29.0.1", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.0.0", + "jest-environment-node": "^29.0.1", + "jest-haste-map": "^29.0.1", + "jest-leak-detector": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-resolve": "^29.0.1", + "jest-runtime": "^29.0.1", + "jest-util": "^29.0.1", + "jest-watcher": "^29.0.1", + "jest-worker": "^29.0.1", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + } + }, + "jest-runtime": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.0.1.tgz", + "integrity": "sha512-yDgz5OE0Rm44PUAfTqwA6cDFnTYnVcYbRpPECsokSASQ0I5RXpnKPVr2g0CYZWKzbsXqqtmM7TIk7CAutZJ7gQ==", + "dev": true, + "requires": { + "@jest/environment": "^29.0.1", + "@jest/fake-timers": "^29.0.1", + "@jest/globals": "^29.0.1", + "@jest/source-map": "^29.0.0", + "@jest/test-result": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-mock": "^29.0.1", + "jest-regex-util": "^29.0.0", + "jest-resolve": "^29.0.1", + "jest-snapshot": "^29.0.1", + "jest-util": "^29.0.1", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.0.1.tgz", + "integrity": "sha512-OuYGp+lsh7RhB3DDX36z/pzrGm2F740e5ERG9PQpJyDknCRtWdhaehBQyMqDnsQdKkvC2zOcetcxskiHjO7e8Q==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.0.1", + "@jest/transform": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.0.1", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.0.1", + "jest-get-type": "^29.0.0", + "jest-haste-map": "^29.0.1", + "jest-matcher-utils": "^29.0.1", + "jest-message-util": "^29.0.1", + "jest-util": "^29.0.1", + "natural-compare": "^1.4.0", + "pretty-format": "^29.0.1", + "semver": "^7.3.5" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "jest-util": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.0.1.tgz", + "integrity": "sha512-GIWkgNfkeA9d84rORDHPGGTFBrRD13A38QVSKE0bVrGSnoR1KDn8Kqz+0yI5kezMgbT/7zrWaruWP1Kbghlb2A==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.0.1.tgz", + "integrity": "sha512-mS4q7F738YXZFWBPqE+NjHU/gEOs7IBIFQ8i9zq5EO691cLrUbLhFq4larf8/lNcmauRO71tn/+DTW2y+MrLow==", + "dev": true, + "requires": { + "@jest/types": "^29.0.1", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.0.0", + "leven": "^3.1.0", + "pretty-format": "^29.0.1" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + } + } + }, + "jest-watcher": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.0.1.tgz", + "integrity": "sha512-0LBWDL3sZ+vyHRYxjqm2irhfwhUXHonjLSbd0oDeGq44U1e1uUh3icWNXYF8HO/UEnOoa6+OJDncLUXP2Hdg9A==", + "dev": true, + "requires": { + "@jest/test-result": "^29.0.1", + "@jest/types": "^29.0.1", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^29.0.1", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.0.1.tgz", + "integrity": "sha512-+B/2/8WW7goit7qVezG9vnI1QP3dlmuzi2W0zxazAQQ8dcDIA63dDn6j4pjOGBARha/ZevcwYQtNIzCySbS7fQ==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "requires": { + "semver": "^6.0.0" + } + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + }, + "dependencies": { + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + } + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz", + "integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "pretty-format": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.0.1.tgz", + "integrity": "sha512-iTHy3QZMzuL484mSTYbQIM1AHhEQsH8mXWS2/vd2yFBYnG3EBqGiMONo28PlPgrW7P/8s/1ISv+y7WH306l8cw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.0.0", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "resolve.exports": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.0.tgz", + "integrity": "sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "stack-utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", + "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.2.0.tgz", + "integrity": "sha512-6sXEzV5+I5j8Bmq9/vUphGRM/RJNT9SCURJLjwfOg51heRtguGWDzcaBlgAzKhQa0EVNpPEKzQuBwZ8S8WaCeQ==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "terminal-link": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", + "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", + "dev": true, + "requires": { + "ansi-escapes": "^4.2.1", + "supports-hyperlinks": "^2.0.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.0.0" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..e04bb07 --- /dev/null +++ b/package.json @@ -0,0 +1,9 @@ +{ + "type": "module", + "devDependencies": { + "jest": "^29.0.1" + }, + "scripts": { + "test": "jest" + } +} diff --git a/packs/jtcs-premade-display-scenes.db b/packs/jtcs-premade-display-scenes.db new file mode 100644 index 0000000..1255d23 --- /dev/null +++ b/packs/jtcs-premade-display-scenes.db @@ -0,0 +1,2 @@ +{"name":"Display","active":false,"navigation":true,"navOrder":0,"navName":"","img":"modules/Journal-to-Canvas-Slideshow/assets/FramesBG.png","foreground":null,"thumb":"data:image/png;base64,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","width":2000,"height":2000,"padding":0,"initial":{"x":1205,"y":1045,"scale":0.32},"backgroundColor":"#202020","gridType":0,"grid":100,"shiftX":0,"shiftY":0,"gridColor":"#000000","gridAlpha":0.2,"gridDistance":5,"gridUnits":"ft","tokenVision":false,"fogExploration":false,"fogReset":1659839149129,"globalLight":true,"globalLightThreshold":null,"darkness":0,"drawings":[],"tokens":[],"lights":[],"notes":[],"sounds":[],"templates":[],"tiles":[{"_id":"NLLvdeFPp2pBx3v6","img":"/modules/journal-to-canvas-slideshow/assets/Bounding_Tile.webp","width":2000,"height":1125,"x":0,"y":437.5,"z":100,"rotation":0,"alpha":1,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{}},{"_id":"0bZbcLBkydz1wS3M","img":"modules/journal-to-canvas-slideshow/demo-images/pd20-360102-num.webp","width":1800,"height":1443.1,"x":100,"y":278.46153846153845,"z":100,"rotation":0,"alpha":1,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{}}],"walls":[],"playlist":null,"playlistSound":null,"journal":null,"weather":"","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"journal-to-canvas-slideshow":{"slideshowTiles":[{"id":"NLLvdeFPp2pBx3v6","displayName":"frameTile","isBoundingTile":true,"linkedBoundingTile":""},{"id":"0bZbcLBkydz1wS3M","displayName":"displayTile","isBoundingTile":false,"linkedBoundingTile":""}],"defaultArtTileID":"0bZbcLBkydz1wS3M"},"core":{"sourceId":"Scene.oFmiUCX9yecjiqAl"}},"_id":"TNeiA03pFhWGh8h5"} +{"name":"Premade Gallery Scene","active":false,"navigation":true,"navOrder":0,"navName":"","img":"modules/journal-to-canvas-slideshow/assets/FramesBG.png","foreground":null,"thumb":"data:image/png;base64,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","width":3000,"height":1700,"padding":0.25,"initial":{"x":2727,"y":1439,"scale":0.23},"backgroundColor":"#212121","gridType":1,"grid":100,"shiftX":0,"shiftY":0,"gridColor":"#ffffff","gridAlpha":0,"gridDistance":5,"gridUnits":"ft","tokenVision":true,"fogExploration":true,"fogReset":1664333575819,"globalLight":false,"globalLightThreshold":null,"darkness":0,"drawings":[],"tokens":[],"lights":[],"notes":[],"sounds":[],"templates":[],"tiles":[{"_id":"10nUs7lBCbqkq7Qd","img":"modules/journal-to-canvas-slideshow/assets/Character-Frames.png","width":3000,"height":1700,"x":800,"y":500,"z":201,"rotation":0,"alpha":1,"tint":"#ffffff","hidden":false,"locked":true,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"YcKMYikyEtAvCTl3","img":"modules/journal-to-canvas-slideshow/demo-images/pd42-63143351-rob.webp","width":620,"height":454,"x":963,"y":1233.4846153846154,"z":200,"rotation":0,"alpha":1,"tint":null,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}},"object":{"zIndex":300},"zIndex":300},{"_id":"akZZaaFEzoxZufZ8","img":"modules/journal-to-canvas-slideshow/assets/Bounding_Tile.webp","width":620,"height":1121,"x":963,"y":900,"z":101,"rotation":180,"alpha":1,"tint":null,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"bHSJpGZUmeHJ8OeT","img":"modules/journal-to-canvas-slideshow/demo-images/pd23-011-jj.webp","width":713,"height":1038.8,"x":1600,"y":917.6069230769231,"z":100,"rotation":0,"alpha":1,"tint":null,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"Ru5lYPRXm8m3EGwA","img":"modules/journal-to-canvas-slideshow/assets/Bounding_Tile.webp","width":713,"height":1274,"x":1600,"y":800,"z":103,"rotation":0,"alpha":1,"tint":"#ff0000","hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"f4gtM4BDlotUFhTJ","img":"modules/journal-to-canvas-slideshow/assets/Bounding_Tile.webp","width":611,"height":1170,"x":2340,"y":900,"z":98,"rotation":0,"alpha":1,"tint":null,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"sZtLVGZNAs979Eun","img":"modules/journal-to-canvas-slideshow/demo-images/pd19-20091_1.webp","width":611,"height":406.1,"x":2340,"y":1281.96,"z":100,"rotation":0,"alpha":1,"tint":null,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{"monks-active-tiles":{"active":false,"restriction":"all","controlled":"all","trigger":"enter","pertoken":false,"minrequired":0,"chance":100,"actions":[]},"tokenmagic":{}}},{"_id":"dmjMY9csyiWtQM6M","img":"modules/journal-to-canvas-slideshow/demo-images/pd20-360102-num.webp","width":750,"height":616.2,"x":3013,"y":1123.798076923077,"z":100,"rotation":0,"alpha":1,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{}},{"_id":"uNmT1zSreNqANa8F","img":"modules/journal-to-canvas-slideshow/assets/Bounding_Tile.webp","width":750,"height":843.8,"x":3013,"y":1010,"z":100,"rotation":0,"alpha":1,"hidden":false,"locked":false,"overhead":false,"occlusion":{"mode":1,"alpha":0},"video":{"loop":true,"autoplay":true,"volume":0},"flags":{}}],"walls":[],"playlist":null,"playlistSound":null,"journal":null,"weather":"","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"journal-to-canvas-slideshow":{"defaultArtTileID":"YcKMYikyEtAvCTl3","slideshowTiles":[{"id":"YcKMYikyEtAvCTl3","displayName":"Art Tile 1","isBoundingTile":false,"linkedBoundingTile":"akZZaaFEzoxZufZ8"},{"id":"bHSJpGZUmeHJ8OeT","displayName":"Art Tile 2","isBoundingTile":false,"linkedBoundingTile":"Ru5lYPRXm8m3EGwA"},{"id":"sZtLVGZNAs979Eun","displayName":"Art Tile 3","isBoundingTile":false,"linkedBoundingTile":"f4gtM4BDlotUFhTJ"},{"id":"akZZaaFEzoxZufZ8","displayName":"Frame Tile A","isBoundingTile":true},{"id":"Ru5lYPRXm8m3EGwA","displayName":"Frame Tile B","isBoundingTile":true},{"id":"f4gtM4BDlotUFhTJ","displayName":"Frame Tile C","isBoundingTile":true},{"id":"dmjMY9csyiWtQM6M","displayName":"Art Tile 4","isBoundingTile":false,"linkedBoundingTile":"uNmT1zSreNqANa8F"},{"id":"uNmT1zSreNqANa8F","displayName":"Frame Tile D","isBoundingTile":true}]},"core":{"sourceId":"Scene.5ut0hKPd8N6YisDm"}},"_id":"xQZR86kbSU9xusuY"} diff --git a/packs/jtcs-premade-display-scenes/000005.ldb b/packs/jtcs-premade-display-scenes/000005.ldb new file mode 100644 index 0000000..946b143 Binary files /dev/null and b/packs/jtcs-premade-display-scenes/000005.ldb differ diff --git a/packs/jtcs-premade-display-scenes/000008.log b/packs/jtcs-premade-display-scenes/000008.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-premade-display-scenes/CURRENT b/packs/jtcs-premade-display-scenes/CURRENT new file mode 100644 index 0000000..f7753e2 --- /dev/null +++ b/packs/jtcs-premade-display-scenes/CURRENT @@ -0,0 +1 @@ +MANIFEST-000006 diff --git a/packs/jtcs-premade-display-scenes/LOCK b/packs/jtcs-premade-display-scenes/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-premade-display-scenes/LOG b/packs/jtcs-premade-display-scenes/LOG new file mode 100644 index 0000000..55c3c95 --- /dev/null +++ b/packs/jtcs-premade-display-scenes/LOG @@ -0,0 +1,8 @@ +2024/01/28-21:30:42.602202 ffff6efaf0c0 Recovering log #4 +2024/01/28-21:30:42.606423 ffff6efaf0c0 Delete type=3 #2 +2024/01/28-21:30:42.606450 ffff6efaf0c0 Delete type=0 #4 +2024/01/28-21:52:42.254216 ffff6d77f0c0 Level-0 table #9: started +2024/01/28-21:52:42.254447 ffff6d77f0c0 Level-0 table #9: 0 bytes OK +2024/01/28-21:52:42.257169 ffff6d77f0c0 Delete type=0 #7 +2024/01/28-21:52:42.264379 ffff6d77f0c0 Manual compaction at level-0 from '!scenes!TNeiA03pFhWGh8h5' @ 72057594037927935 : 1 .. '!scenes.tiles!xQZR86kbSU9xusuY.uNmT1zSreNqANa8F' @ 0 : 0; will stop at (end) +2024/01/28-21:52:42.264500 ffff6d77f0c0 Manual compaction at level-1 from '!scenes!TNeiA03pFhWGh8h5' @ 72057594037927935 : 1 .. '!scenes.tiles!xQZR86kbSU9xusuY.uNmT1zSreNqANa8F' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-premade-display-scenes/LOG.old b/packs/jtcs-premade-display-scenes/LOG.old new file mode 100644 index 0000000..b249134 --- /dev/null +++ b/packs/jtcs-premade-display-scenes/LOG.old @@ -0,0 +1,5 @@ +2024/01/28-21:29:21.936697 ffff6efaf0c0 Delete type=3 #1 +2024/01/28-21:30:22.962266 ffff6d77f0c0 Level-0 table #5: started +2024/01/28-21:30:22.967312 ffff6d77f0c0 Level-0 table #5: 104736 bytes OK +2024/01/28-21:30:22.970386 ffff6d77f0c0 Delete type=0 #3 +2024/01/28-21:30:22.975099 ffff6d77f0c0 Manual compaction at level-0 from '!scenes!TNeiA03pFhWGh8h5' @ 72057594037927935 : 1 .. '!scenes.tiles!xQZR86kbSU9xusuY.uNmT1zSreNqANa8F' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-premade-display-scenes/MANIFEST-000006 b/packs/jtcs-premade-display-scenes/MANIFEST-000006 new file mode 100644 index 0000000..49fbc9b Binary files /dev/null and b/packs/jtcs-premade-display-scenes/MANIFEST-000006 differ diff --git a/packs/jtcs-premade-journal-entries.db b/packs/jtcs-premade-journal-entries.db new file mode 100644 index 0000000..4fbcecf --- /dev/null +++ b/packs/jtcs-premade-journal-entries.db @@ -0,0 +1,2 @@ +{"name":"Art","content":"

 

\n

Tristique et Egestas Quis Ipsum

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ornare arcu dui vivamus arcu. Iaculis nunc sed augue lacus viverra vitae congue. Et tortor at risus viverra adipiscing at in tellus.

\n

\n

 

\n

Massa eget egestas purus viverra accumsan. Luctus accumsan tortor posuere ac ut consequat semper viverra nam. Aliquam etiam erat velit scelerisque in dictum non consectetur a. Cras tincidunt lobortis feugiat vivamus at augue eget arcu. Vitae suscipit tellus mauris a. At elementum eu facilisis sed odio morbi quis commodo odio. Consectetur purus ut faucibus pulvinar elementum integer enim neque.

\n

 

\n

Aliquet Lectus Proin

\n

 

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Imperdiet sed euismod nisi porta lorem. Pharetra pharetra massa massa ultricies. Gravida neque convallis a cras. Tristique risus nec feugiat in fermentum. Adipiscing commodo elit at imperdiet dui.

\n

 

\n

\n

 

\n

Nibh sit Amet

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Nibh sit amet commodo nulla facilisi nullam vehicula ipsum. Felis eget nunc lobortis mattis. Quam lacus suspendisse faucibus interdum posuere lorem. Adipiscing commodo elit at imperdiet dui. Semper quis lectus nulla at. Leo in vitae turpis massa sed elementum.

\n

 

\n

\n

Feugiat in Fermentum

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Donec adipiscing tristique risus nec feugiat in. Ultrices vitae auctor eu augue ut lectus arcu bibendum at. Tempus imperdiet nulla malesuada pellentesque elit eget. Habitasse platea dictumst quisque sagittis purus. Facilisis leo vel fringilla est.

\n

\n

\n

 

\n

Tempus imperdiet

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. At quis risus sed vulputate. Vestibulum lectus mauris ultrices eros in cursus turpis massa tincidunt. Dictum at tempor commodo ullamcorper a lacus vestibulum sed. Aenean vel elit scelerisque mauris. Aliquam sem fringilla ut morbi tincidunt augue interdum.

\n


\n

\n

 

\n

Vestibulum lectus mauris

\n

\n

 

\n

Sagittis nisl rhoncus mattis rhoncus. ie ac feugiat sed lectus vestibulum mattis ullamcorper velit sed. At volutpat diam ut venenatis tellus in. Consequat interdum varius sit amet mattis vulputate enim nulla. Et malesuada fames ac turpis egestas maecenas pharetra convallis posuere. Vitae aliquet nec ullamcorper sit amet risus nullam. Elit duis tristique sollicitudin nibh sit amet. Lorem mollis aliquam ut porttitor leo.

\n

 

\n

Eu volutpat odio facilisis mauris sit amet massa vitae. Massa sed elementum tempus egestas sed sed risus pretium quam. Id neque aliquam vestibulum morbi blandit. Sed id semper risus in hendrerit gravida. Ut consequat semper viverra nam libero. A lacus vestibulum sed arcu non. Quam elementum pulvinar etiam non quam lacus. Neque laoreet suspendisse interdum consectetur. Platea dictumst quisque sagittis purus sit amet volutpat consequat. Euismod in pellentesque massa placerat.

\n

 

\n

\n

 

\n

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Vivamus at augue eget arcu dictum. Non nisi est sit amet facilisis magna etiam. Nec dui nunc mattis enim ut tellus elementum sagittis vitae. Nunc mattis enim ut tellus elementum sagittis vitae. Dui id ornare arcu odio.

\n

 

\n

","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"JournalEntry.wwfHk5WNE5tkayrB"},"world":{"displayLocation":"anyScene"},"exportSource":{"world":"moduletestworld","system":"cyphersystem","coreVersion":"9.269","systemVersion":"1.32.3"},"journal-to-canvas-slideshow":{"showControls":true}},"_id":"HC7VLG78jjZDmV1j"} +{"name":"Display Journal","content":"","img":"modules/journal-to-canvas-slideshow/demo-images/pd19-20091_1.webp","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"JournalEntry.nZtJ4p6xOoDldYTH"}},"_id":"icIWzXBCIddrrqa7"} diff --git a/packs/jtcs-premade-journal-entries/000005.ldb b/packs/jtcs-premade-journal-entries/000005.ldb new file mode 100644 index 0000000..82b3eba Binary files /dev/null and b/packs/jtcs-premade-journal-entries/000005.ldb differ diff --git a/packs/jtcs-premade-journal-entries/000008.log b/packs/jtcs-premade-journal-entries/000008.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-premade-journal-entries/CURRENT b/packs/jtcs-premade-journal-entries/CURRENT new file mode 100644 index 0000000..f7753e2 --- /dev/null +++ b/packs/jtcs-premade-journal-entries/CURRENT @@ -0,0 +1 @@ +MANIFEST-000006 diff --git a/packs/jtcs-premade-journal-entries/LOCK b/packs/jtcs-premade-journal-entries/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-premade-journal-entries/LOG b/packs/jtcs-premade-journal-entries/LOG new file mode 100644 index 0000000..c8fba8f --- /dev/null +++ b/packs/jtcs-premade-journal-entries/LOG @@ -0,0 +1,8 @@ +2024/01/28-21:30:42.609711 ffff6f7bf0c0 Recovering log #4 +2024/01/28-21:30:42.612534 ffff6f7bf0c0 Delete type=3 #2 +2024/01/28-21:30:42.612559 ffff6f7bf0c0 Delete type=0 #4 +2024/01/28-21:52:42.257240 ffff6d77f0c0 Level-0 table #9: started +2024/01/28-21:52:42.257315 ffff6d77f0c0 Level-0 table #9: 0 bytes OK +2024/01/28-21:52:42.259240 ffff6d77f0c0 Delete type=0 #7 +2024/01/28-21:52:42.264393 ffff6d77f0c0 Manual compaction at level-0 from '!journal!HC7VLG78jjZDmV1j' @ 72057594037927935 : 1 .. '!journal!icIWzXBCIddrrqa7' @ 0 : 0; will stop at (end) +2024/01/28-21:52:42.264621 ffff6d77f0c0 Manual compaction at level-1 from '!journal!HC7VLG78jjZDmV1j' @ 72057594037927935 : 1 .. '!journal!icIWzXBCIddrrqa7' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-premade-journal-entries/LOG.old b/packs/jtcs-premade-journal-entries/LOG.old new file mode 100644 index 0000000..c03c479 --- /dev/null +++ b/packs/jtcs-premade-journal-entries/LOG.old @@ -0,0 +1,5 @@ +2024/01/28-21:29:21.964026 ffff6efaf0c0 Delete type=3 #1 +2024/01/28-21:30:22.960215 ffff6d77f0c0 Level-0 table #5: started +2024/01/28-21:30:22.961363 ffff6d77f0c0 Level-0 table #5: 3413 bytes OK +2024/01/28-21:30:22.962176 ffff6d77f0c0 Delete type=0 #3 +2024/01/28-21:30:22.975083 ffff6d77f0c0 Manual compaction at level-0 from '!journal!HC7VLG78jjZDmV1j' @ 72057594037927935 : 1 .. '!journal!icIWzXBCIddrrqa7' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-premade-journal-entries/MANIFEST-000006 b/packs/jtcs-premade-journal-entries/MANIFEST-000006 new file mode 100644 index 0000000..d2e8c23 Binary files /dev/null and b/packs/jtcs-premade-journal-entries/MANIFEST-000006 differ diff --git a/packs/jtcs-premade-journals.db b/packs/jtcs-premade-journals.db new file mode 100644 index 0000000..5d870ca --- /dev/null +++ b/packs/jtcs-premade-journals.db @@ -0,0 +1,2 @@ +{"name":"Art","content":"

\n

 

\n

\n

\n

","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"journal-to-canvas-slideshow":{"clickableImages":[{"name":"modules/journal-to-canvas-slideshow/demo-images/pd19-20091_1","scenesData":[{"sceneID":"oFmiUCX9yecjiqAl","selectedTileID":"0bZbcLBkydz1wS3M"}],"displayLocation":"displayScene"},{"name":"modules/journal-to-canvas-slideshow/demo-images/pd20-360102-num","scenesData":[{"sceneID":"XVGuxZbIURYCqfrG","selectedTileID":"d0nJR0i1fdnb1w3h"}]},{"name":"modules/journal-to-canvas-slideshow/demo-images/pd23-011-jj","scenesData":[{"sceneID":"oFmiUCX9yecjiqAl","selectedTileID":"0bZbcLBkydz1wS3M"}]}],"showControls":true},"core":{"sourceId":"JournalEntry.wAR3aNF5T1hgUAhb"}},"_id":"vs0CjdTc62AcAxW1"} +{"name":"Display Journal","content":"","img":"modules/journal-to-canvas-slideshow/demo-images/pd19-20091_1.webp","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"JournalEntry.nZtJ4p6xOoDldYTH"}},"_id":"xTyXBKG7oXwOJQIV"} diff --git a/packs/jtcs-utility-macros.db b/packs/jtcs-utility-macros.db new file mode 100644 index 0000000..87c1d35 --- /dev/null +++ b/packs/jtcs-utility-macros.db @@ -0,0 +1,5 @@ +{"name":"Tile Toggle Locked Status","type":"script","author":"FLvo1qb3UkC40yKG","img":"icons/svg/padlock.svg","scope":"global","command":"// originally by Anthony Vadala and Shawn Dibble\n//https://github.com/foundry-vtt-community/macros/blob/main/misc/tile_toggle_locked_status.js\n\n//Simple macro to loop through ALL SELECTED TILES and toggle their locked status.\n//In other words:\n //If an individual tile is unlocked, this macro will lock it.\n //If an individual tile is locked, this macro will unlock it.\n\n const tiles = canvas.background.controlled.length ? canvas.background.controlled : canvas.foreground.controlled;\n const updates = tiles.map(tile => ({ _id: tile.id, locked: !tile.data.locked }));\n canvas.scene.updateEmbeddedDocuments(\"Tile\", updates);","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"Macro.FcmAATox0lTQuPvn"}},"_id":"DO68MDHqstmJJh2S"} +{"name":"Open JTCS-Art-Gallery Config","type":"script","author":"FLvo1qb3UkC40yKG","img":"icons/svg/card-hand.svg","scope":"global","command":"if(!game.JTCSlideshowConfig) game.JTCSlideshowConfig = new SlideshowConfig()\nif(!game.JTCSlideshowConfig.rendered)\n game.JTCSlideshowConfig.render(true)\nelse\n game.JTCSlideshowConfig.bringToTop()","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.WWxPRiTaL2mg8lql"}},"_id":"RyYAGTXo26QPi6ZB"} +{"name":"Tile Scale Manager","type":"script","author":"FLvo1qb3UkC40yKG","img":"modules/journal-to-canvas-slideshow/assets/contract.svg","scope":"global","command":"(()=>{\n \n let buttonValues = {\n \"scaleLarger\": 2.0,\n \"scaleSmaller\": 0.5\n}\nlet axisValues = [\" \", \"width\", \"height\"]\n\nrenderDialog()\n\n\nfunction processButtonObject(){\n \n return object;\n}\n\nfunction renderDialog(){\n let options= {\n width: 600,\n id: \"JTCS-custom-dialog\",\n classes: [\"dialog-grid-3\"]\n }\n let dialog;\n let object = {}\n for(const key in buttonValues){\n let value = buttonValues[key]\n \n for(const axis of axisValues){\n object[`${key}${axis}`] = {\n label: `x${value} ${axis}`,\n callback: ()=> {\n scaleAndMove(value, axis)\n dialog.render(true)\n \n }\n }\n \n }\n }\n dialog = new Dialog({\n title: 'Tile Controls',\n content: `click the buttons below to scale a tile`,\n buttons: object,\n}, options).render(true);\n}\n\nfunction scaleAndMove(scale, axis){\n game.JTCS.utils.manager.scaleControlledTiles(scale, axis)\n renderDialog()\n}\n \n})();","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"Macro.s2LknTdxNh4N69p4"}},"_id":"UCJcOT7EH8WaUhAE"} +{"name":"Tile Movement Manager","type":"script","author":"FLvo1qb3UkC40yKG","img":"modules/journal-to-canvas-slideshow/assets/multi-directions.svg","scope":"global","command":"(()=>{\n \n let buttonValues = {\n \"moveTiny\": 0.5,\n \"moveSmall\": 1.0,\n \"moveLarge\": 10,\n \"moveHuge\": 20,\n}\nlet directions = [1, -1]\nlet axisValues = [\"x\", \"y\"]\n\nrenderDialog()\n\n\n\n\nfunction renderDialog(){\n let options= {\n width: 600,\n id: \"JTCS-custom-dialog\",\n classes: [\"dialog-grid\"]\n }\n\n let dialog;\n let object = {};\n for(const key in buttonValues){\n for(const axis of axisValues){\n for(const direction of directions){\n let value = buttonValues[key] * direction\n let label = getDirectionName(axis, value) + \" \" + buttonValues[key].toString()\n let directionName = (direction >= 1)\n object[label] = {\n label: label,\n callback: ()=> {\n scaleAndMove(value, axis)\n dialog.render(true)\n \n }\n }\n } \n }\n \n}\n\n dialog = new Dialog({\n title: 'Tile Controls',\n content: `click the buttons below to move a tile`,\n buttons: object,\n }, options).render(true)\n \n}\n\nfunction getDirectionName(axis, amount){\n let directionName = \"\";\n if(axis === \"x\"){\n if(amount < 0){\n directionName = \"left\"\n \n } else{\n \n directionName = \"right\"\n }\n }\n if(axis === \"y\"){\n if( amount < 0){\n directionName = \"up\"\n \n }else{\n directionName = \"down\"\n \n }\n \n \n }\n return directionName;\n \n \n}\n\nfunction scaleAndMove(amount, direction){\n game.JTCS.utils.manager.moveControlledTiles(amount, direction)\n //renderDialog()\n}\n \n})();","folder":null,"sort":0,"permission":{"default":0,"FLvo1qb3UkC40yKG":3},"flags":{"core":{"sourceId":"Macro.gKU43tEiULxIIRVw"}},"_id":"moGBJ148Fro1pbUP"} +{"name":"Fit Tile to Scene","type":"script","author":"FLvo1qb3UkC40yKG","img":"icons/skills/targeting/crosshair-bars-yellow.webp","scope":"global","command":"renderDialog();\n\nfunction renderDialog(){\n //filter out things that aren't tiles\n let viewedScene = game.scenes.viewed;\n let sceneTiles = canvas.background.controlled.filter((obj)=> obj.document.documentName === 'Tile');\n if(sceneTiles.length === 0){\n ui.notifications.warn(\"No tiles selected\");\n return;\n }\n let d = new Dialog({\n title: 'Fit Tile to Scene',\n content: `Along which dimension should the tile fit?`,\n buttons: {\n width: {\n label: 'Width',\n callback: ()=>{fitTo('width')}\n },\n\n height: {\n label: 'Height',\n callback: ()=>{fitTo('height')}\n }, \n both: {\n label: 'Both',\n callback: ()=>{fitTo('both')}\n },\n }\n}).render(true);\n}\n\nfunction fitTo(whichDimension){\n //filter out things that aren't tiles\n let viewedScene = game.scenes.viewed;\n\n let sceneTiles = canvas.background.controlled.filter((obj)=> obj.document.documentName === 'Tile');\n let dimensions = viewedScene.dimensions;\n\n //get array of tile ids for update\n let tileData = sceneTiles.map((tile) => {return{_id: tile.data._id}})\n let updateData = {}\n \n switch (whichDimension) {\n case 'width':\n // code\n updateData.width = dimensions.sceneRect.width;\n break;\n case 'height':\n updateData.height = dimensions.sceneRect.height;\n break;\n case 'both':\n updateData.width = dimensions.sceneWidth;\n updateData.height = dimensions.sceneHeight;\n break;\n }\n tileData = tileData.map((data)=> {return {...data, ...updateData}})\n updateTiles(tileData)\n}\n\nfunction updateTiles(updateData){\n let viewedScene = game.scenes.viewed;\n\n viewedScene.updateEmbeddedDocuments(\"Tile\", updateData);\n}","folder":null,"sort":0,"permission":{"default":0,"xjKGzWFbtOifTl9H":3,"FLvo1qb3UkC40yKG":3},"flags":{"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.KWmUj6RKO4bzYdk8"}},"_id":"wbmIqlU6VPkKlTPP"} diff --git a/packs/jtcs-utility-macros/000005.ldb b/packs/jtcs-utility-macros/000005.ldb new file mode 100644 index 0000000..6768712 Binary files /dev/null and b/packs/jtcs-utility-macros/000005.ldb differ diff --git a/packs/jtcs-utility-macros/000008.log b/packs/jtcs-utility-macros/000008.log new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-utility-macros/CURRENT b/packs/jtcs-utility-macros/CURRENT new file mode 100644 index 0000000..f7753e2 --- /dev/null +++ b/packs/jtcs-utility-macros/CURRENT @@ -0,0 +1 @@ +MANIFEST-000006 diff --git a/packs/jtcs-utility-macros/LOCK b/packs/jtcs-utility-macros/LOCK new file mode 100644 index 0000000..e69de29 diff --git a/packs/jtcs-utility-macros/LOG b/packs/jtcs-utility-macros/LOG new file mode 100644 index 0000000..20beebe --- /dev/null +++ b/packs/jtcs-utility-macros/LOG @@ -0,0 +1,8 @@ +2024/01/28-21:30:42.613745 ffff6efaf0c0 Recovering log #4 +2024/01/28-21:30:42.615845 ffff6efaf0c0 Delete type=3 #2 +2024/01/28-21:30:42.615863 ffff6efaf0c0 Delete type=0 #4 +2024/01/28-21:52:42.259282 ffff6d77f0c0 Level-0 table #9: started +2024/01/28-21:52:42.259306 ffff6d77f0c0 Level-0 table #9: 0 bytes OK +2024/01/28-21:52:42.260622 ffff6d77f0c0 Delete type=0 #7 +2024/01/28-21:52:42.264407 ffff6d77f0c0 Manual compaction at level-0 from '!macros!DO68MDHqstmJJh2S' @ 72057594037927935 : 1 .. '!macros!wbmIqlU6VPkKlTPP' @ 0 : 0; will stop at (end) +2024/01/28-21:52:42.264611 ffff6d77f0c0 Manual compaction at level-1 from '!macros!DO68MDHqstmJJh2S' @ 72057594037927935 : 1 .. '!macros!wbmIqlU6VPkKlTPP' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-utility-macros/LOG.old b/packs/jtcs-utility-macros/LOG.old new file mode 100644 index 0000000..bb89095 --- /dev/null +++ b/packs/jtcs-utility-macros/LOG.old @@ -0,0 +1,5 @@ +2024/01/28-21:29:21.977996 ffff6df8f0c0 Delete type=3 #1 +2024/01/28-21:30:22.970645 ffff6d77f0c0 Level-0 table #5: started +2024/01/28-21:30:22.972148 ffff6d77f0c0 Level-0 table #5: 3863 bytes OK +2024/01/28-21:30:22.974952 ffff6d77f0c0 Delete type=0 #3 +2024/01/28-21:30:22.975222 ffff6d77f0c0 Manual compaction at level-0 from '!macros!DO68MDHqstmJJh2S' @ 72057594037927935 : 1 .. '!macros!wbmIqlU6VPkKlTPP' @ 0 : 0; will stop at (end) diff --git a/packs/jtcs-utility-macros/MANIFEST-000006 b/packs/jtcs-utility-macros/MANIFEST-000006 new file mode 100644 index 0000000..77d4803 Binary files /dev/null and b/packs/jtcs-utility-macros/MANIFEST-000006 differ diff --git a/release-notes.md b/release-notes.md new file mode 100644 index 0000000..ddac860 --- /dev/null +++ b/release-notes.md @@ -0,0 +1,205 @@ +# Release Notes + +## v0.2.4 - 10/17/2022 + +### MAJOR + +#### Removed + +- Due to changes in FoundryV10 making it easier to see indicators, removed the "fadeTileButton" from the Scene Gallery Config (in v10 version only) + +### MINOR + +#### Added + +- Added tests to work with [Quench](https://github.com/Ethaks/FVTT-Quench) + +## v.0.2.3 + +### Added + +#### Major + +- Added "auto-view" and "auto-activate" options in settings for "Art Journal" and "Art Scene" + +## v.0.2.2 + +**CHANGED** +**Major** + +- Added support for v10 with backwards compatibility +- v10 version **REQUIRES** right-click on sheet images instead of left-click, to avoid interfering with default JournalEntryPage functionality when clicking on an image + +## v.0.2.1 - 10/06/2022 + +**PATCHED** + +**Major** + +- Fixed bug with src not being fetched from video elements + +## v.0.2.0 - 10/05/2022 + +**CHANGED** + +**Major** + +- Improved image-share controls in actor, item, and journal sheets + - Various Display methods can be accessed and activated by hovering over an image and clicking on one of these controls + - the original "click on the image to display it on the canvas" functionality remains intact. + +**ADDED** + +**Major** + +- Scene Gallery Config + - Configuration settings +- Settings and Customization + - settings application that can be launched from multiple locations and includes customization options + - colored overlays shown on tiles on the canvas whenever you hover a connected UI item, to ensure you can easily find them. - overlay colors are customizable + - Color customization of elements UI in JTCS Art Gallery apps, including a default light and dark theme. +- Compendiums + - Compendium pack of macros with featuring utilities to make moving and scaling tiles easier + - Compendium pack of premade scenes displaying demo setups of Gallery tiles, including a scene meant to act as your default "Display Scene" + - Compendium pack of Journal Entries including a scene meant to act as your default "Display Journal" + +**REMOVED** + +- Tile Tool Controls added by the module, including controls/dialog to change Display Method and share URL image. +- The above tools have been replaced by "Scene Gallery Config" App, which can be accessed from the same place. + +## **v0.1.8 - v0.1.9** - 2022-01-16 + +**CHANGED** + +- Updated for Foundry version 9. Check in "releases" for the version still compatible with version 8. + +## **v0.1.7** - 2021-08-26 + +**CHANGED** + +- Integrated features from pull requests, such as item images now being able to be clicked on and displayed. (Thanks, @DarKDinDoN !) +- Added setting to hide or change how "Toggle Display Location" button in journal header displays. + +## **v0.1.6** - 2021-06-09 + +Updated module to work with Foundry v8.6 + +## **v0.1.5** - 2021-05-23 + +**ADDED** + +**Major** + +- NEW: Ability to _right click_ on actor sheet character images to display them the same as journal images. + +- NEW: Ability to display Journal-to-Canvas-Slideshow tools within a dialog rather than as tile control tools. + +See the settings for **Use Actor Sheet Images** and **Hide Tile Buttons** in the updated module settings below. +!["New Settings"](https://i.imgur.com/AfHLPSG.png) + +### Default Tile Control Tools + +The default tile control tools with the Hide Tile Buttons setting disabled. + +A new button is there called "Switch Display Location" that will display a dialog that allows you to switch display locations without needing to go into the module's settings. + +!["Switch Display Location Button"](https://i.imgur.com/3XLHTku.png) + +!["Switch Display Location Dialog"](https://i.imgur.com/CBSidW0.png) + +**Note**: Journal entries now have a button in the header that allows you to switch the display location as well. + +**Note**: You can switch away from the tile control tools and then back again to "refresh" if you enable or disable the Hide Tile Buttons setting. + +--- + +### Tile Control Tools with Hide Tile Buttons Turned On + +With the Hide Tile Buttons setting enabled, all Journal-to-Canvas-Slideshow buttons will not be displayed except for the "Clear Display" button, and a new button that says "Show Slideshow Config". + +To show the other functions, click on the button in the tile control tools that says "Show Slideshow Config". + +!["Hide Tile Buttons Setting Turned On"](https://i.imgur.com/6a7oxpt.png) + +The following dialog will appear with buttons with all the functionality, such as creating Display and Bounding Tiles, Setting a URL image, and switching between display locations. + +!["Hide Tile Buttons Dialog"](https://i.imgur.com/u5DWfMc.png) + +--- + +**CHANGED**: + +- Many features now work with VIEWED scene rather than ACTIVE scene, such as the bounding tiles. + +--- + +## **v0.1.4** - 2021-03-19 + +**ADDED** + +**Major**: + +- NEW: Bounding Tiles implemented by @Occidio +- NEW: Display Tiles that along with Bounding Tiles can be added to _any scene_. +- NEW: Display images via copy-pasting URL feature implemented by @p4535992 +- NEW: Display in Window feature alternative implemented by @DarKDinDoN +- NEW: Extra settings to accomodate the above new features -- please check the settings menu and reselect your prefered settings. + +**Changes**: + +**Major:** + +- Special "Display Tiles" now created via button in Tile controls menu. Flagged by script, so no longer have to be very first tile in scene. +- **Warning**: Please replace regular tile in pre-made Display Scenes with new Display Tile, else the script will not detect them. + +## **v0.1.3** - 2021-01-22 + +**ADDED** + +**Major**: + +- NEW: Added option to display journal images in a window rather than display scene + +- NEW: Module settings + +## **v0.1.2** - 2021-01-03 + +**Major:** + +- Fixed an incompatability issue with the Call of Cthulhu 7e (CoC7) system. + +## **v0.1.1** - 2020-12-28 + +### **Added** + +**Major:** + +- Added "Clear Display" button in Tiles scene control buttons. Will set 'slideshow' tile to a transparent image. + +**Minor:** + +- More visual effects when hovering over and clicking images in journal, for more user feedback +- Changed cursor to pointer on hover of journal images + +### **Changes** + +- Clicking on image in journal no longer activates the 'Display' scene if a different scene is active. Plan to add functionality later to toggle this behavior. + +(Red arrow pointing at new 'Clear Display' button') +!["Location of clear button"](https://i.imgur.com/aPtU9QL.jpg) + +!["Showing off updates"](https://media2.giphy.com/media/sIKIPBhN3c5vLPVxGu/giphy.gif) + +# Roadmap + +- I next intend to add a way to more easily toggle between the various different settings (Display in Window vs Display in Scene, etc.) without needing to go all the way to the settings menu. + +- I may possibily implement a way to have multiple Display Tiles in a single scene, but I will need to think of the best way to implement this. + +- Clicking on image in journal no longer activates the 'Display' scene if a different scene is active. Plan to add functionality later to toggle this behavior. + +(Red arrow pointing at new 'Clear Display' button') +!["Location of clear button"](https://i.imgur.com/aPtU9QL.jpg) + +!["Showing off updates"](https://media2.giphy.com/media/sIKIPBhN3c5vLPVxGu/giphy.gif) diff --git a/scripts/ClickImageInJournal.js b/scripts/ClickImageInJournal.js new file mode 100644 index 0000000..4026e62 --- /dev/null +++ b/scripts/ClickImageInJournal.js @@ -0,0 +1,23 @@ +function setEventListeners(html, app) { + //look for the images and videos with the clickable image class, and add event listeners for being hovered over (to highlight and dehighlight), + //and event listeners for the "displayImage" function when clicked + // execute(html, app); + wait().then(execute.bind(null, [html, app])); +} + +function wait(callback) { + return new Promise(function (resolve, reject) { + resolve(); + }); +} + +function execute(args) { + let [html, app] = args; + + // this one is for actor sheets. Right click to keep it from conflicting with the default behavior of selecting an image for the actor. + if (checkSettingEquals("useActorSheetImages", true)) { + html.find(".rightClickableImage").each((i, img) => { + img.addEventListener("contextmenu", (event) => determineLocation(event, app), false); + }); + } +} diff --git a/scripts/ClickImageInJournal.test.js b/scripts/ClickImageInJournal.test.js new file mode 100644 index 0000000..53e10dd --- /dev/null +++ b/scripts/ClickImageInJournal.test.js @@ -0,0 +1,9 @@ + +//what component are you testing +//const +//what is the actual output +const actual = 'what is the actual output?'; +const expected = 'What is the expected output?'; +//what is the expected output +//assert.equal(actual, expected, 'What should the feature do?'); +//test('what should the feature do?', () => { expect(actual).toBe(expected);}); \ No newline at end of file diff --git a/scripts/DragDropIntoJournal.js b/scripts/DragDropIntoJournal.js new file mode 100644 index 0000000..d4bb3ce --- /dev/null +++ b/scripts/DragDropIntoJournal.js @@ -0,0 +1,81 @@ +Hooks.once("init", async function () {}); +let currentJournalId; + +async function addImage(app, url, currentJournalId) { + var journalEntry; + //create image tag with url of item + var containerParagraph; + var image = new Image(); + //append the child to the body of the journal entry -- gotta figure out how to add it to the journal entry specifically + var journalDiv = document.getElementById(currentJournalId); + var journalForm = journalDiv.getElementsByTagName("form")[0]; + var editorContent = journalForm.getElementsByClassName("editor-content")[0]; + containerParagraph = journalForm.getElementsByClassName("editor-content")[0].querySelector("p"); //appendChild(containerParagraph); + if (containerParagraph == null) { + containerParagraph = document.createElement("p"); + journalForm.getElementsByClassName("editor-content")[0].appendChild(containerParagraph); + } + containerParagraph.appendChild(image); + image.src = url; + app.object.data.content = editorContent.innerHTML; + app.object.update({ + content: app.object.data.content, + }); + + await app.submit(); +} + +async function handleDrop(app, event, currentJournalId) { + event.preventDefault(); + + var files = event.dataTransfer.files; + for (let f of files) { + checkSource(app, f, currentJournalId); + } +} +//implemented and tweaked these methods from DragUpload by cswendrowski +//https://github.com/cswendrowski/FoundryVTT-Drag-Upload/blob/master/dragupload.js + +async function checkSource(app, file, currentJournalId) { + if (typeof ForgeVTT != "undefined" && ForgeVTT.usingTheForge) { + source = "forgevtt"; + } else { + var source = "data"; + } + let response; + if (file.isExternalUrl) { + response = { + path: file.url, + }; + } else { + response = await FilePicker.upload( + source, + game.settings.get("journal-to-canvas-slideshow", "imageSaveLocation"), + file, + {} + ); + } + addImage(app, response.path, currentJournalId); +} + +Hooks.on("renderJournalSheet", (app, html, options) => { + currentJournalId = html[0].id; + var journalDiv = html[0]; + if (!journalDiv.querySelector("div.editor")) { + return; + } + journalDiv.querySelector("div.editor").addEventListener("drop", (event) => { + handleDrop(app, event, currentJournalId); + }); +}); + +Hooks.once("init", async function () { + game.settings.register("DragDropIntoJournal", "imageSaveLocation", { + name: "Image Save Location", + hint: "Where in your Data folder would you like to save the images you drag into journal entries? Input the file path to your prefered folder here.", + scope: "client", + config: true, + type: String, + default: "", + }); +}); diff --git a/scripts/SheetImageActions.js b/scripts/SheetImageActions.js new file mode 100644 index 0000000..5b8242f --- /dev/null +++ b/scripts/SheetImageActions.js @@ -0,0 +1,284 @@ +"use strict"; + +import { HelperFunctions } from "./classes/HelperFunctions.js"; +import { SheetImageDataController } from "./SheetImageDataController.js"; +import { SheetImageApp } from "./SheetImageApp.js"; +import { SlideshowConfig } from "./SlideshowConfig.js"; +import { extraActions } from "./data/SlideshowConfigActions.js"; +import { ArtTileManager } from "./classes/ArtTileManager.js"; +import { ImageDisplayManager } from "./classes/ImageDisplayManager.js"; +import { JTCSSettingsApplication } from "./classes/JTCSSettingsApplication.js"; +import { universalInterfaceActions as UIA } from "./data/Universal-Actions.js"; + +export const sheetControls = [ + { + action: "sheet.click.toggleImageControls", + tooltip: "Toggle the image controls on this sheet", + icon: "fas fa-sliders-h", + toggle: true, + activeOn: "showControls", + }, + { + action: "sheet.click.fadeJournal", + icon: "fas fa-eye-slash", + tooltip: "Fade sheet background to see canvas", + // toggle: true, + }, + { + action: "sheet.click.openSlideshowConfig", + tooltip: "open Gallery Tile Config Application for the current scene", + icon: "fas fa-cubes", + }, + { + action: "sheet.click.openSettingsApp", + tooltip: "open JTCS Art Gallery Settings", + icon: "fas fa-cog", + }, + + // { + // action: "sheet.click.toggleInstructions", + // tooltip: "show or hide contextual instructions", + // icon: "fas fa-info", + // }, + // { + // action: "sheet.click.showURLShareDialog", + // tooltip: "Share a URL Image with your players", + // icon: "fas fa-external-link", + // }, +]; +export const dedicatedDisplayControls = [ + { + label: "view", + callback: (controls) => {}, + }, + { + label: "showToAll", + callback: () => {}, + }, + { + label: "showToSome", + callback: () => {}, + }, +]; +export const sheetImageActions = { + sheet: { + click: { + fadeJournal: { + onClick: async (event, options) => { + await SheetImageApp.addFadeStylesToSheet(event); + UIA.toggleActiveStyles(event); + }, + }, + // setDefaultTileInScene: { + // onClick: async (event, options) => { + // //TODO: + // //? show the tiles + // }, + // }, + toggleImageControls: { + onClick: async (event, options) => { + let { app } = options; + let journalEntry = app.object; + let currentSetting = await HelperFunctions.getFlagValue( + journalEntry, + "showControls", + "", + false + ); + //if current setting is false, or doesn't exist, set it to true + if (!currentSetting || currentSetting.length === 0) { + await HelperFunctions.setFlagValue( + journalEntry, + "showControls", + true + ); + } else { + await HelperFunctions.setFlagValue( + journalEntry, + "showControls", + false + ); + } + + UIA.toggleActiveStyles(event); + }, + }, + changeControlsPosition: { + onClick: async (event, options) => { + let positions = [ + "top-left", + "top-right", + "bottom-left", + "bottom-right", + ]; + await HelperFunctions.getFlagValue(journalEntry, "controlsPosition"); + await HelperFunctions.setFlagValue( + journalEntry, + "controlsPosition", + true + ); + }, + }, + openSlideshowConfig: { + onClick: async () => { + await UIA.renderAnotherApp("JTCSlideshowConfig", SlideshowConfig); + }, + }, + openSettingsApp: { + onClick: async () => { + await UIA.renderAnotherApp( + "JTCSSettingsApp", + JTCSSettingsApplication + ); + }, + }, + showURLShareDialog: { + onClick: async (event, options) => + await extraActions.showURLShareDialog(event, options), + }, + toggleInstructions: { + onClick: (event, option) => { + const parentElement = event.currentTarget.closest("editor"); + UIA.toggleShowAnotherElement(event, { + parentItem: parentElement, + targetClassSelector: "instructions", + }); + }, + }, + }, + }, + image: { + click: { + sendImageDataToDisplay: { + onClick: async (event, options) => { + // event.stopPropagation(); + options.method = "anyScene"; + // event.stopImmediatePropagation(); + + // bundle all the necessary data into an object + let sheetImageData = + await SheetImageDataController.wrapSheetImageData(options); + + await ImageDisplayManager.determineDisplayMethod(sheetImageData); + }, + }, + }, + hover: { + showTileIndicator: { + onHover: async (event, options) => { + //!get the default tile + //get the default art tile in this scene + let tileID = await ArtTileManager.getDefaultArtTileID( + game.scenes.viewed + ); + let isLeave = + event.type === "mouseleave" || event.type === "mouseout" + ? true + : false; + let tile = await ArtTileManager.getTileObjectByID(tileID); + if (isLeave) { + await game.JTCS.indicatorUtils.hideTileIndicator(tile); + } else { + await game.JTCS.indicatorUtils.showTileIndicator(tile); + } + }, + }, + }, + }, + tileButton: { + hover: { + showTileIndicator: { + onHover: async (event, data = {}) => { + let isLeave = + event.type === "mouseout" || event.type === "mouseleave"; + let tileID = event.currentTarget.dataset.id; //this should grab the value from the radio button itself + let tile = await game.JTCS.tileUtils.getTileObjectByID(tileID); + + if (isLeave) { + await game.JTCS.indicatorUtils.hideTileIndicator(tile); + } else { + await game.JTCS.indicatorUtils.showTileIndicator(tile); + } + }, + }, + }, + click: { + displayImageOnTile: { + onClick: async (event, options) => { + let { imgElement } = options; + let tileID = event.currentTarget.dataset.id; + if (event.ctrlKey) { + const appName = "JTCSlideshowConfig"; + await UIA.renderAnotherApp(appName, SlideshowConfig); + if (game[appName]) { + const configElement = game[appName].element; + configElement.focus(); + + const tileItem = configElement.find(`[data-id='${tileID}']`); + if (tileItem && tileItem[0]) { + tileItem[0].scrollIntoView(); + tileItem[0].focus(); + } + } + return; + } + + let url = ImageDisplayManager.getImageSource(imgElement); + const frameID = await ArtTileManager.getGalleryTileDataFromID( + tileID, + "linkedBoundingTile" + ); + await ImageDisplayManager.updateTileObjectTexture( + tileID, + frameID, + url, + "anyScene" + ); + + //once clicked, hide the buttons + UIA.toggleHideAncestor(event, { + ...options, + ancestorSelector: "#displayTileButtons", + }); + }, + }, + }, + }, + displayActionButton: { + click: { + sendImageDataToDisplay: { + onClick: async (event, options) => { + let { app, html, imgElement } = options; + let method = event.currentTarget.dataset.method; + let sheetImageData = + await SheetImageDataController.wrapSheetImageData({ + ...options, + method: method, + imgElement, + }); + + await ImageDisplayManager.determineDisplayMethod(sheetImageData); + + //TODO: add this functionality in later for prompting the user for what to do next + // if (method === "journalEntry" || method === "artScene") { + // let property = method === "journalEntry" ? "journal" : "scene"; + // const autoActivate = await HelperFunctions.getSettingValue( + // "artGallerySettings", + // `dedicatedDisplayData.${property}.autoActivate` + // ); + // if (!autoActivate) { + // UIA.toggleShowAnotherElement(event, options); + // UIA.toggleActiveStyles(event); + // } + // } + }, + }, + revealTileButtons: { + onClick: async (event, options) => { + UIA.toggleShowAnotherElement(event, options); + UIA.toggleActiveStyles(event); + }, + }, + }, + }, +}; diff --git a/scripts/SheetImageApp.js b/scripts/SheetImageApp.js new file mode 100644 index 0000000..39edb28 --- /dev/null +++ b/scripts/SheetImageApp.js @@ -0,0 +1,315 @@ +"use strict"; +import { log } from "./debug-mode.js"; +import { HelperFunctions } from "./classes/HelperFunctions.js"; +import { sheetImageActions, sheetControls } from "./SheetImageActions.js"; +import { SheetImageDataController } from "./SheetImageDataController.js"; +import { artTileManager, helpers } from "./data/ModuleManager.js"; +import { ArtTileManager } from "./classes/ArtTileManager.js"; +import { universalInterfaceActions } from "./data/Universal-Actions.js"; +export class SheetImageApp { + static displayMethods = [ + { + name: "anyScene", + icon: "fas fa-vector-square", + tooltip: "choose Art Tile in current scene to display image on", + }, + { + name: "window", + icon: "fas fa-external-link-alt", + tooltip: "Display image in pop-out window", + }, + { + name: "journalEntry", + icon: "fas fa-book-open", + tooltip: "display image in your chosen 'Art Journal'", + }, + { + name: "artScene", + icon: "far fa-image", + tooltip: "display image in your chosen 'Art Scene'", + }, + ]; + + /** + * + * @param {*} app - the application (sheet) that this is being called from + * @param {*} html + */ + static async applyImageClasses(app, html) { + if (game.user.isGM) { + const whichSheets = await HelperFunctions.getSettingValue( + "artGallerySettings", + "sheetSettings.modularChoices" + ); + const doc = app.document; + let onThisSheet = await HelperFunctions.getFlagValue( + doc, + "showControls", + "", + false + ); + + let documentName = doc.documentName; + documentName = documentName.charAt(0).toLowerCase() + documentName.slice(1); + // if (documentName === "item" && doc.parent) { + // //if it's an embedded item in a sheet + // return; + // } + // for v10 + + if (game.version >= 10) { + if (documentName === "journalEntryPage") { + documentName = "journalEntry"; + onThisSheet = await HelperFunctions.getFlagValue( + doc.parent, + "showControls", + "", + false + ); + } + } + let selectorString = "img, video, .lightbox-image"; + if (whichSheets[documentName] || onThisSheet === true) { + if (onThisSheet) { + if (documentName === "journalEntry" && game.version < 10) { + html.find(selectorString).addClass("clickableImage"); + } else { + html.find(selectorString).addClass("rightClickableImage"); + } + //inject the controls into every image that has the clickableImage or rightClickableImage classes + + Array.from( + html.find(".clickableImage, .rightClickableImage") + ).forEach((img) => SheetImageApp.injectImageControls(img, app)); + } + } + if (doc.documentName !== "JournalEntryPage") { + SheetImageApp.injectSheetWideControls(app); + } + } + } + + static async applySheetFadeSettings(journalSheet) { + //get opacity, and whether or not journals should be faded + let opacityValue = ( + await game.JTCS.utils.getSettingValue( + "artGallerySettings", + "sheetFadeOpacityData" + ) + ).value; + let shouldFadeImages = ( + await game.JTCS.utils.getSettingValue( + "artGallerySettings", + "fadeSheetImagesData" + ) + ).chosen; + //set a CSS variable on the journal sheet to grab the opacity in css + let sheetElement = journalSheet.element; + sheetElement[0].style.setProperty("--journal-fade", opacityValue + "%"); + + //set the window content's data-attribute to "data-fade-all" so it fades the journal's opacity, and not just the background color + if (shouldFadeImages === "fadeAll") { + sheetElement.find(".window-content").attr("data-fade-all", true); + // sheetElementStyle.setProperty("--fade-all", true); + } + } + + /** + * When the journal sheet renders, we're going to add controls over each image + * @param {HTMLElement} imgElement - the image HTML element + * @param {*} journalSheet - the journal sheet we're searching within + */ + static async injectImageControls(imgElement, journalSheet) { + let template = "modules/journal-to-canvas-slideshow/templates/image-controls.hbs"; + // game.JTCS.templates["image-controls"] + const defaultArtTileID = await ArtTileManager.getDefaultArtTileID(); + + const imageName = await SheetImageDataController.convertImageSourceToID( + imgElement + ); + imgElement.dataset.name = imageName; + + //get the art tiles in the scene + let displayTiles = await ArtTileManager.getSceneSlideshowTiles("art", true); + displayTiles = displayTiles.filter((tile) => !tile.missing); + displayTiles = displayTiles.map((tile) => { + return { + tile: tile, + randomID: foundry.utils.randomID(), + }; + }); + + let users = game.users.contents; + + let renderHtml = await renderTemplate(template, { + currentSceneName: game.scenes.viewed.name, + displayMethods: SheetImageApp.displayMethods, + displayTiles: displayTiles, + defaultArtTileID: defaultArtTileID, + imgPath: imageName, + users: users, + // ...imageFlagData, + }); + + //wrap each image in a clickableImageContainer + $(imgElement).wrap("
"); + + $(imgElement).parent().append(renderHtml); + await SheetImageApp.activateImageEventListeners({ + controlsContainer: $(imgElement).parent(), + journalSheet: journalSheet, + imgElement: imgElement, + }); + } + static async injectSheetWideControls(journalSheet) { + let template = game.JTCS.templates["sheet-wide-controls"]; + await SheetImageApp.applySheetFadeSettings(journalSheet); + let isActive = await HelperFunctions.getFlagValue( + journalSheet.document, + "showControls", + "", + false + ); + let controlsData = sheetControls.map((control) => + control.toggle ? { ...control, active: isActive } : control + ); + let renderHtml = await renderTemplate(template, { + controls: controlsData, + isActive, + }); + let selector = ".window-content"; + if (journalSheet.document.documentName === "JournalEntryPage") { + selector = ".journal-page-content"; + } + let $editorElement = $(journalSheet.element[0].querySelector(selector)); + $editorElement.prepend(renderHtml); + let controlsContainer = $("#sheet-controls"); + await SheetImageApp.activateSheetWideEventListeners({ + controlsContainer, + journalSheet, + isActive, + }); + } + + static async activateSheetWideEventListeners(options) { + const { journalSheet, isActive } = options; + const controlsContainer = journalSheet.element.find("#sheet-controls"); + $(controlsContainer) + .off("click", "[data-action]") + .on("click", "[data-action]", async (event) => { + SheetImageApp.handleAction(event, journalSheet, "action", false); + }); + const controlsToggleButton = $(controlsContainer).find( + "[data-action='sheet.click.toggleImageControls']" + )[0]; + if (isActive) { + $(controlsToggleButton).addClass("active"); + } + universalInterfaceActions.toggleHideAllSiblings(null, controlsToggleButton); + } + + // handle any interaction event + static async handleAction(event, journalSheet, actionType = "action", isItem = true) { + event.preventDefault(); + let targetElement = $(event.currentTarget); + let imgElement; + + //"isItem" stands for if it's a sheet-wide control or an item-specific control + if (isItem) { + //if our target element is not an image, get the closest image from our clickableImageContainer parent + //else just get the current target itself + + if ( + targetElement.prop("nodeName") !== "IMG" || + targetElement.prop("nodeName") !== "VIDEO" + ) { + imgElement = targetElement[0] + .closest(".clickableImageContainer") + .querySelector("img, video"); + } else { + imgElement = targetElement[0]; + } + //if our target element is a label, get the input before it instead + targetElement.prop("nodeName") === "LABEL" && + (targetElement = targetElement.prev()); + } + + let action = targetElement.data()[actionType]; + let handlerPropertyString = "onClick"; + + switch (actionType) { + case "hoverAction": + handlerPropertyString = "onHover"; + break; + case "changeAction": + handlerPropertyString = "onChange"; + break; + } + let actionData = getProperty(sheetImageActions, action); + + if (actionData && actionData.hasOwnProperty(handlerPropertyString)) { + //call the event handler stored on this object + let options = { + action: action, + app: journalSheet, + html: journalSheet.element, + ...(imgElement && { + parentItem: imgElement.closest(".clickableImageContainer"), + }), + imgElement: imgElement, + }; + actionData[handlerPropertyString](event, options); + } + } + + /** + * + * @param data - the data object + */ + static async activateImageEventListeners(data) { + let { journalSheet, imgElement, controlsContainer } = data; + let html = journalSheet.element; + //add data actions to the images + $(imgElement).attr("data-hover-action", "image.hover.showTileIndicator"); + $(imgElement).attr("data-action", "image.click.sendImageDataToDisplay"); + + $(controlsContainer) + .off("click contextmenu", "[data-action]") + .on( + "click contextmenu", + "[data-action]", + async (event) => + await SheetImageApp.handleAction(event, journalSheet, "action") + ); + $(controlsContainer) + .off("mouseenter mouseleave", "[data-hover-action]") + .on( + "mouseenter mouseleave", + "[data-hover-action]", + async (event) => + await SheetImageApp.handleAction(event, journalSheet, "hoverAction") + ); + $(controlsContainer) + .off("change", "[data-change-action]") + .on( + "change", + "[data-change-action]", + async (event) => + await SheetImageApp.handleAction(event, journalSheet, "changeAction") + ); + } + + static async addFadeStylesToSheet(event) { + event.preventDefault(); + + let windowContent = event.currentTarget.closest(".window-content"); + let faded = + windowContent.classList.contains("fade") || + windowContent.classList.contains("fade-all"); + + if (faded) { + windowContent.classList.remove("fade"); + } else { + windowContent.classList.add("fade"); + } + } +} diff --git a/scripts/SheetImageDataController.js b/scripts/SheetImageDataController.js new file mode 100644 index 0000000..ed1631a --- /dev/null +++ b/scripts/SheetImageDataController.js @@ -0,0 +1,163 @@ +import { HelperFunctions } from "./classes/HelperFunctions.js"; +import { log, MODULE_ID } from "./debug-mode.js"; +// Model: The backend that contains all the data logic +// View: The frontend or graphical user interface (GUI) +// Controller: The brains of the application that controls how data is displayed +// import { artGalleryDefaultSettings } from "./settings.js"; + +export class SheetImageDataController { + static checkFlags(documentCollectionName, flagName = "journal-to-canvas-slideshow") { + let flaggedJournalEntries = game[documentCollectionName].contents.filter( + (entry) => entry.data.flags["journal-to-canvas-slideshow"] + ); + return flaggedJournalEntries; + } + static async getAllFlaggedSheets() { + //get every journal entry with flags associated with this + let flaggedJournals = SheetImageDataController.checkFlags("journal"); + let flaggedActors = SheetImageDataController.checkFlags("actors"); + let flaggedItems = SheetImageDataController.checkFlags("items"); + let flaggedDocs = [...flaggedJournals, ...flaggedActors, ...flaggedItems]; + return flaggedDocs; + } + + /** + *Remove any tile data from docs that has an id that was deleted + * @param {*} removedTileID - remove any reference to a tile with this ID + */ + static async removeTileDataFromDocs(removedTileID, sceneID) { + let flaggedDocs = await SheetImageDataController.getAllFlaggedSheets(); + for (let doc of flaggedDocs) { + let clickableImages = await HelperFunctions.getFlagValue( + doc, + "clickableImages" + ); + clickableImages = clickableImages.map((item) => { + return { + ...item, + scenesData: item.scenesData.filter( + (sceneData) => removedTileID !== sceneData.selectedTileID + ), + }; + }); + + // map array of objects, and filter out any scenesdata objects in scenesDay array that hold the removed tile id + await HelperFunctions.setFlagValue(doc, "clickableImages", clickableImages); + } + } + + /** + * Store image data in flags + * @param {App} journalSheet - the journal sheet whose images we're storing in the flag + * @param {HTMLElement} imgElement - the image HTML element + * @param {Obj} newImgData - the data being stored + */ + static async updateImageFlags(journalSheet, imgElement, newImgData) { + let journalEntry = journalSheet.object; + let imageName = await SheetImageDataController.convertImageSourceToID(imgElement); + let clickableImages = await HelperFunctions.getFlagValue( + journalEntry, + "clickableImages" + ); + let foundImage = clickableImages.find((imgData) => imgData.name === imageName); + if (foundImage) { + setProperty(foundImage, newImgData); + // clickableImages = clickableImages.map((imgData) => { + // // if the ids match, update the matching one with the new displayName + // return imgData.name === imageName ? { ...imgData, ...newImgData } : imgData; //else just return the original + // }); + } else { + clickableImages.push({ name: imageName, ...newImgData }); + } + + await HelperFunctions.setFlagValue( + journalEntry, + "clickableImages", + clickableImages + ); + await HelperFunctions.setFlagValue( + journalEntry, + "linkedImageTilesByID", + clickableImages + ); + } + + // /** + // * Return data specific to the current viewed scene for the particular image in the journal entry, which should change when the scene does + // * @param {Object} imageFlagData - the data from the flag for this particular image in the journal entry + // * @returns the data specific to the current viewed scene + // */ + // static async getSceneSpecificImageData(imageFlagData) { + // let currentSceneID = game.scenes.viewed.data._id; + // return imageFlagData.scenesData?.find((obj) => obj.sceneID === currentSceneID); //want to get the specific data for the current scene + // } + + static async getGalleryTileIDsFromImage(imageElement, journalSheet) { + let imageData = await SheetImageDataController.getJournalImageFlagData( + journalSheet.object, + imageElement + ); + if (!imageData) { + console.error("could not get data from that sheet and element"); + return; + } + + let flaggedTiles = await game.JTCS.tileUtils.getSceneSlideshowTiles("", true); + let frameTileID = await game.JTCS.tileUtils.getLinkedFrameID( + artTileID, + flaggedTiles + ); + if (!artTileID) { + console.error("Image data has no tile ID"); + return; + } + return { + artTileID: artTileID, + frameTileID: frameTileID, + }; + } + + /** + * Convert the image's path without extention to use as as an identifier to store it in flags + * @param {Element} imgElement - the image element + * @returns a string path + */ + static async convertImageSourceToID(imgElement) { + let name = imgElement.getAttribute("src"); + name = name.replace(/\.(gif|jpe?g|tiff?|png|webp|bmp)/g, ""); + return name; + } + /** + * get the flag data for this image in this journal entry + * @param {Document} journalEntry - the journal entry whose flags we're looking in + * @param {Element} imgElement - an HTML Image Element + * @returns an object containing the data saved in the flags + */ + static async getJournalImageFlagData(journalEntry, imgElement) { + let clickableImages = + (await HelperFunctions.getFlagValue(journalEntry, "clickableImages")) || []; + // let clickableImages = (await journalEntry.getFlag("journal-to-canvas-slideshow", "clickableImages")) || []; + let foundData = clickableImages.find((imgData) => { + return imgData.name == imgElement.dataset["name"]; + }); + return { journalID: journalEntry.id, ...foundData }; + } + + /** + * Returns all the necessary data in a bundled object + * @param {Object} options - bundled options like the app, the html element, and the imgElement + * @returns Object + */ + static async wrapSheetImageData(options) { + let { app, html, imgElement } = options; + // let imageData = await SheetImageDataController.getJournalImageFlagData(app.object, imgElement); + // let galleryTileIDs = await SheetImageDataController.getGalleryTileIDsFromImage(imgElement, app); + let sheetImageData = { + imageElement: imgElement, + // ...imageData, + // ...galleryTileIDs, + method: options.method || "window", //if we don't have a location set, default to window + }; + return sheetImageData; + } +} diff --git a/scripts/SlideshowConfig.js b/scripts/SlideshowConfig.js new file mode 100644 index 0000000..233eb18 --- /dev/null +++ b/scripts/SlideshowConfig.js @@ -0,0 +1,215 @@ +"use strict"; +import { log, MODULE_ID } from "./debug-mode.js"; +import { slideshowDefaultSettingsData } from "./data/SlideshowConfigActions.js"; +import { Popover } from "./classes/PopoverGenerator.js"; +import { HelperFunctions } from "./classes/HelperFunctions.js"; + +export class SlideshowConfig extends Application { + constructor(data = {}) { + super(); + this.data = data; + this.element.find(".window-content").attr("data-fade-all"); + } + + /** + * @override + */ + async _render(force, options = {}) { + return super._render(force, options); + } + + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["form"], + popOut: true, + resizable: true, + height: 500, + template: + "modules/journal-to-canvas-slideshow/templates/scene-tiles-config.hbs", + id: "slideshow-config", + title: "Scene Gallery Config", + scrollY: ["ul"], + }); + } + + //for saving tab layouts and such + renderWithData() { + this.render(true, this.data); + } + hideButtons(html) { + Array.from( + html.querySelectorAll(".tile-list-item .actions .icon-button") + ).forEach((btn) => { + btn = $(btn); + let data = btn.data(); + let parentData = btn.closest(".tile-list-item").data(); + let type = parentData.type; + let action = data.action.split(".").pop(); + let itemActionsObject = + slideshowDefaultSettingsData.itemActions.click.actions; + let filteredActionKeys = []; + let v9onlyKeys = []; + Object.keys(itemActionsObject).forEach((itemAction) => { + if (getProperty(itemActionsObject, itemAction).v9Only === true) { + v9onlyKeys.push(itemAction); + } + }); + Object.keys(itemActionsObject).forEach((itemAction) => { + if (getProperty(itemActionsObject, itemAction).artTileOnly) { + filteredActionKeys.push(itemAction); + } + }); + filteredActionKeys.forEach((itemAction) => { + if (type === "frame" && itemAction === action) { + btn.css({ display: "none" }); + } + }); + v9onlyKeys.forEach((itemAction) => { + if (game.version >= 10 && itemAction === action) { + btn.css({ display: "none" }); + } + }); + }); + } + /** + * Handles all types of actions (click, hover, etc.) and finds their relevant functions + * @param {Event} event - the passed in event that triggered this + * @param {String} actionType - the type of action "action, hover action, changeAction, etc" + */ + async handleAction(event, actionType = "action") { + event.preventDefault(); + + let targetElement = $(event.currentTarget); + //if our target element is a label, get the input before it instead + targetElement.prop("nodeName") === "LABEL" && + (targetElement = targetElement.prev()); + + let action = targetElement.data()[actionType]; + let handlerPropertyString = "onClick"; + + let parentLI = targetElement[0].closest(".tile-list-item, .popover"); + let tileID = parentLI?.dataset?.id; + + switch (actionType) { + case "hoverAction": + handlerPropertyString = "onHover"; + break; + case "changeAction": + handlerPropertyString = "onChange"; + break; + } + let actionData = getProperty(slideshowDefaultSettingsData, action); + + if (actionData && actionData.hasOwnProperty(handlerPropertyString)) { + //call the event handler stored on this object + let app = game.JTCSlideshowConfig; + let options = { + action: action, + tileID: tileID, + parentLI: parentLI, + app: app, + html: app.element, + }; + actionData[handlerPropertyString](event, options); + } + } + + async getData() { + //return data to template + + let artTileDataArray = await game.JTCS.tileUtils.getSceneSlideshowTiles( + "art", + true + ); + let frameTileDataArray = await game.JTCS.tileUtils.getSceneSlideshowTiles( + "frame", + true + ); + + let unlinkedTileIDs = await game.JTCS.tileUtils.getUnlinkedTileIDs([ + ...artTileDataArray, + ...frameTileDataArray, + ]); + let areConfigInstructionsVisible = await HelperFunctions.getSettingValue( + "areConfigInstructionsVisible" + ); + + let allJournals = game.journal.contents; + let artJournal = await game.JTCS.utils.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.journal.value" + ); + + let artJournalData = { + options: allJournals, + value: artJournal, + }; + + let allScenes = await game.JTCS.tileUtils.getAllScenesWithSlideshowData(); + let artScene = await game.JTCS.utils.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.scene.value" + ); + + let defaultArtTileID = await game.JTCS.tileUtils.manager.getDefaultArtTileID(); + let artSceneData = { + options: allScenes, + value: artScene, + }; + + return { + shouldActivateDisplayScene: this.shouldActivateDisplayScene, + artTiles: artTileDataArray, + defaultArtTileID, + frameTiles: frameTileDataArray, + unlinkedTiles: unlinkedTileIDs, + currentSceneName: game.scenes.viewed.name, + artSceneData: artSceneData, + artJournalData: artJournalData, + partials: game.JTCS.templates, + areConfigInstructionsVisible, + settingsData: slideshowDefaultSettingsData, + ...this.data, + }; + } + + async activateListeners(html) { + // super.activateListeners(html); + html = $(html[0].closest(".window-app")); + this.hideButtons(html[0]); + html.find(".window-content").attr("data-fade-all", true); + // await this.setUIColors(html); + // this._handleToggle(html); + + html.off("click").on( + "click", + "[data-action]", + async (event) => await this.handleAction(event, "action", html) + ); + + let hoverEventString = "mouseenter mouseleave"; + let hoverActionSelectorString = `[data-hover-action], + [data-hover-action] + label`; + + html.off(hoverEventString, hoverActionSelectorString).on( + hoverEventString, + hoverActionSelectorString, + async (event) => await this.handleAction(event, "hoverAction") + ); + + // let tooltipActionSelectorString = "[data-tooltip]"; + // html.off(hoverEventString, tooltipActionSelectorString).on( + // hoverEventString, + // tooltipActionSelectorString, + // async (event) => await Popover.generateTooltip(event, html, ".popover, .tile-list-item", "tooltip") + // ); + + html.off("change").on( + "change", + "[data-change-action]", + async (event) => await this.handleAction(event, "changeAction") + ); + } +} + +window.SlideshowConfig = SlideshowConfig; diff --git a/scripts/SlideshowLayer.js b/scripts/SlideshowLayer.js new file mode 100644 index 0000000..34dcd76 --- /dev/null +++ b/scripts/SlideshowLayer.js @@ -0,0 +1,4 @@ + +export class SlideshowLayer extends TilesLayer{ + +} \ No newline at end of file diff --git a/scripts/classes/ArtTileManager.js b/scripts/classes/ArtTileManager.js new file mode 100644 index 0000000..a471425 --- /dev/null +++ b/scripts/classes/ArtTileManager.js @@ -0,0 +1,597 @@ +import { MODULE_ID } from "../debug-mode.js"; +import { HelperFunctions, HelperFunctions as HF } from "./HelperFunctions.js"; +import { ImageDisplayManager } from "./ImageDisplayManager.js"; +/** + * This class manages the Art and Bounding Tiles, creating them, showing them in the Config, and + * getting and setting their values + */ + +export class ArtTileManager { + /** + * + * @param {String} oldTileID - the id of a tile that's now missing + * @param {String newTileID - the id of a new tile we're linking it to + */ + static async updateTileDataID(oldTileID, newTileID) { + //this is an array of objects + let tileDataArray = await ArtTileManager.getSceneSlideshowTiles("", false); + + //find object with id + + let index = tileDataArray.findIndex((tileData) => tileData.id === oldTileID); + let tileObject = tileDataArray[index]; //.find((tileData) => tileData.id === oldTileID); + tileObject = { ...tileObject, id: newTileID }; + + //here we ensure that the info for the linked tiles are also getting updated. Find ones that match our old ID, and update them to our new id + + //replace the object at its original index with the object w/ the new id + if (tileObject && index !== undefined) { + tileDataArray.splice(index, 1, tileObject); + + if (tileObject.isBoundingTile) { + ArtTileManager.updateLinkedArtTiles(oldTileID, newTileID, tileDataArray); + } + + await ArtTileManager.updateAllSceneTileFlags(tileDataArray); + } + } + + static async updateLinkedArtTiles(oldFrameID, newFrameID, tileDataArray) { + //get art tiles that had us as their bounding tile + let linkedArtTiles = []; + let updatedLinkedArtTiles = []; + linkedArtTiles = tileDataArray.filter( + (tileData) => tileData.linkedBoundingTile === oldFrameID + ); + //update them with our new ID + updatedLinkedArtTiles = linkedArtTiles.map((tileData) => { + return { + ...tileData, + linkedBoundingTile: newFrameID, + }; + }); + updatedLinkedArtTiles.forEach((atData) => { + let atIndex = tileDataArray.findIndex((item) => item.id === atData.id); + tileDataArray.splice(atIndex, 1, atData); + }); + return tileDataArray; + } + static async getDefaultData(isBoundingTile, linkedBoundingTile = "") { + //determine its default name based on whether it's a bounding or display tile + let displayName = isBoundingTile ? "frameTile" : "displayTile"; + displayName = await ArtTileManager.incrementTileDisplayName(displayName); + //increment it if one already exists with that name + return { + displayName: `${displayName}`, + isBoundingTile: isBoundingTile, + linkedBoundingTile: linkedBoundingTile, + }; + } + + static async incrementTileDisplayName(name) { + let finalName = name; + let tileDataArray = await ArtTileManager.getSceneSlideshowTiles(); + let conflictingTile = tileDataArray.find((tileData) => { + return tileData.displayName.includes(name); + }); + if (conflictingTile) { + let digit = conflictingTile.displayName.match(/\d+/g); + if (digit) { + digit = parseInt(digit); + digit++; + } else { + digit = 1; + } + finalName = finalName + digit; + } + return finalName; + } + + static async createTileInScene(isFrameTile) { + let ourScene = game.scenes.viewed; + let pathProperty = isFrameTile ? "frameTilePath" : "artTilePath"; + let imageManager = ImageDisplayManager; + + let imgPath = await HF.getSettingValue( + "artGallerySettings", + `defaultTileImages.paths.${pathProperty}` + ); + if (!imgPath) { + return; + } + const tex = await loadTexture(imgPath); + + let sceneWidth = game.version >= 10 ? ourScene.width : ourScene.data.width; + let sceneHeight = game.version >= 10 ? ourScene.height : ourScene.data.height; + let dimensionObject = imageManager.calculateAspectRatioFit( + tex.width, + tex.height, + sceneWidth, + sceneHeight + ); + + let newTile = await ourScene.createEmbeddedDocuments("Tile", [ + { + img: imgPath, + width: dimensionObject.width, + height: dimensionObject.height, + x: sceneWidth / 2 - dimensionObject.width / 2, + y: sceneHeight / 2 - dimensionObject.height / 2, + }, + ]); + return newTile; + } + + static async createOrFindDefaultFrameTile() { + let frameTile; + let tileDataArray = (await ArtTileManager.getSceneSlideshowTiles()) || []; + if (tileDataArray.length === 0) { + ui.notifications.warn( + "No frame tile detected in scene. Creating new frame tile alongside display tile" + ); + frameTile = await ArtTileManager.createFrameTileObject(); + return frameTile[0].id; + } else { + ui.notifications.warn( + "No frame tile provided to when creating display tile. Linking display tile to first frame tile in scene" + ); + frameTile = tileDataArray[0]; + return frameTile.id; + } + } + + /** + * Return any tiles that have data, but their id doesn't match any tile in the scene + * @param {Array} flaggedTiles - array of tiles with Slideshow data + * @returns array of tiles that have data but aren't connected to a tile in the scene + */ + static async getMissingTiles(flaggedTiles) { + let currentScene = game.scenes.viewed; + let sceneTileIDs = currentScene.tiles.map((tile) => tile.id); + let missingTiles = flaggedTiles.filter( + (tileData) => !sceneTileIDs.includes(tileData.id) + ); + return missingTiles; + } + + /** + * Get tiles that aren't linked to any slideshow data + * @param {Array} flaggedTiles - array of tiles with Slideshow data + * @returns array of IDs of tiles that aren't linked to any slideshow data + */ + static async getUnlinkedTileIDs(flaggedTiles) { + let currentScene = game.scenes.viewed; + let flaggedTileIDs = flaggedTiles.map((tileData) => tileData.id); + let sceneTileIDs = currentScene.tiles.map((tile) => tile.id); + let unlinkedTiles = sceneTileIDs.filter( + (tileID) => !flaggedTileIDs.includes(tileID) + ); + return unlinkedTiles; + } + + /** + * + * @param {String} linkedFrameTileId - the frame tile linked to this art tile, if it is one + * @param {String} tileObjectID - the ID of the tile being created + * @param {Boolean} isBoundingTile - whether or not its a frame tile instead of an art tile + */ + static async createTileData(linkedFrameTileId, tileObjectID, isBoundingTile = false) { + let defaultData = await ArtTileManager.getDefaultData( + isBoundingTile, + linkedFrameTileId + ); + await ArtTileManager.updateSceneTileFlags(defaultData, tileObjectID); + } + + /** + * Create a Tile in the current scene that is linked to the Tile Data we're passing in + * @param {Object} options - the options object + * @param {Boolean} options.isFrameTile - whether or not its a frame tile or an art tile + * @param {String} options.linkedFrameTileID - the ID of the linked frame tile + * @param {String} options.unlinkedDataID - the ID of the unlinked art gallery tile we want to link to a tile in the scene + * @returns the new tile object in the scene that is linked to our previously unlinked data + */ + static async createAndLinkSceneTile( + options = { + isFrameTile: false, + linkedFrameTileID: "", + unlinkedDataID: "", + } + ) { + let { isFrameTile, linkedFrameTileID, unlinkedDataID } = options; + + let newTile = await ArtTileManager.createTileInScene(isFrameTile); + + if (newTile) { + let tileObjectID = newTile[0].id; + + // + if (!unlinkedDataID) { + console.log("Scene Gallery Config - Creating new tile data"); + await ArtTileManager.createTileData( + linkedFrameTileID, + tileObjectID, + false + ); + } else { + console.log( + "Scene Gallery Config | updating already created tile data, and linking it" + ); + await ArtTileManager.updateTileDataID(unlinkedDataID, tileObjectID); + } + } else { + ui.notifications.error("New art gallery tile couldn't be created"); + } + return newTile; + } + + /** + * + * @param {String} linkedFrameTileId - the frame tile linked to this art tile, if it is one + * @param {*} unlinkedDataID - if we're creating a new tile from the config rather than from the tile itself, it may have an unlinkedId + * @returns - the created art tile + */ + static async createArtTileObject(_linkedFrameTileId = "", unlinkedDataID = "") { + let linkedFrameTileId = _linkedFrameTileId; + + let newTile = await ArtTileManager.createTileInScene(false); + if (newTile) { + let tileObjectID = newTile[0].id; + if (!unlinkedDataID) { + await ArtTileManager.createTileData( + linkedFrameTileId, + tileObjectID, + false + ); + } else { + await ArtTileManager.updateTileDataID(unlinkedDataID, tileObjectID); + } + } else { + ui.notifications.error("New display tile couldn't be created"); + } + return newTile; + } + + /** + * @param {String} linkedFrameTileId - the frame tile linked to this art tile, if it is one + * @param {*} unlinkedDataID - if we're creating a new tile from the config rather than from the tile itself, it may have an unlinkedId + * @returns - the created frame tile + * */ + static async createFrameTileObject(unlinkedDataID = "") { + let newTile = await ArtTileManager.createTileInScene(true); + if (newTile) { + let tileObjectID = newTile[0].id; + // let tileObjectID = newTile[0].document.id; + if (!unlinkedDataID) { + await ArtTileManager.createTileData("", tileObjectID, true); + } else { + await ArtTileManager.updateTileDataID(unlinkedDataID, tileObjectID); + } + } else { + ui.notifications.error("New frame tile couldn't be created"); + } + return newTile; + } + + static async convertToNewSystem() { + let currentScene = game.scenes.viewed; + let sceneTiles = currentScene.tiles.contents; + let boundingTile; + let displayTile; + sceneTiles.forEach(async (tile) => { + let flag = await tile.getFlag("journal-to-canvas-slideshow", "name"); + switch (flag.name) { + case "boundingTile": + boundingTile = tile; + break; + case "displayTile": + displayTile = tile; + break; + default: + break; + } + }); + if (boundingTile) { + convertBoundingTile(boundingTile.data); + if (displayTile) { + convertDisplayTile(displayTile.data, boundingTile.id); + } + } else { + if (displayTile) { + convertDisplayTile(displayTile.data); + } + } + } + + static convertBoundingTile(tileData) { + let defaultData = { + displayName: "BoundingTile1", + isBoundingTile: true, + linkedBoundingTile: "", + }; + updateSceneTileFlags(defaultData, tileData.id); + } + static convertDisplayTile(tileData, linkedBoundingTileId = "") { + let defaultData = { + displayName: "DisplayTile1", + isBoundingTile: false, + linkedBoundingTile: linkedBoundingTileId, + }; + updateSceneTileFlags(defaultData, tileData.id); + } + + /** + * Set the default art gallery art tile in this scene + * @param {String} tileID - the id of the art gallery tile that was clicked + * @param {Object} currentScene - the current scene doc + */ + static async setDefaultArtTileID(tileID, currentScene) { + if (!currentScene) currentScene = game.scenes.viewed; + await currentScene.setFlag(MODULE_ID, "defaultArtTileID", tileID); + Hooks.callAll("updateDefaultArtTile", { updateData: tileID, currentScene }); + } + /** + * Gets the default art gallery tile in this scene, or returns the first tile in scene if not found + * returns undefined if that is also not found + * @param {Object} currentScene - the current scene doc + */ + static async getDefaultArtTileID(currentScene) { + if (!currentScene) currentScene = game.scenes.viewed; + + //get all the art tiles in the scene, filtering out the ones that aren't unlinked/missing + let artTiles = ( + await ArtTileManager.getSceneSlideshowTiles("art", true, { + currentSceneID: currentScene.id, + }) + ).filter((item) => !item.missing); + let defaultArtTileID = await currentScene.getFlag(MODULE_ID, "defaultArtTileID"); + + // if the defaultArtTileID stored in our flags is + let shouldReplaceID = false; + const found = artTiles.find((tileData) => tileData.id === defaultArtTileID); + + if (!found) { + //a tile with this ID wasn't in the scene, so replace it with a the first art tile that IS linked, or if there are none, replace it with an empty string + shouldReplaceID = true; + + if (artTiles.length > 0) { + defaultArtTileID = artTiles[0].id; + } else { + defaultArtTileID = ""; + } + } + // should + if (shouldReplaceID) { + await currentScene.setFlag(MODULE_ID, "defaultArtTileID", defaultArtTileID); + } + + return defaultArtTileID; + } + + static async getGalleryTileDataFromID(tileID, property = "", currentSceneID = "") { + if (!currentSceneID) currentSceneID = game.scenes.viewed.current; + let flaggedTiles = await ArtTileManager.getSceneSlideshowTiles("", false, { + currentSceneID, + }); + let ourTile = flaggedTiles.find((data) => data.id === tileID); + if (property) { + if (ourTile) { + return ourTile[property]; + } else { + return ""; + } + } else { + return ourTile; + } + } + + /** + * get tiles that have been stored by this module in a flag on this scene + * @returns array of display tile data stored in "slideshowTiles" tag + */ + static async getSceneSlideshowTiles( + type = "", + shouldCheckIfExists = false, + options = { currentSceneID: "" } + ) { + let { currentSceneID } = options; + let currentScene = game.scenes.viewed; + if (currentSceneID) { + currentScene = game.scenes.get(currentSceneID); + } + let flaggedTiles = + (await currentScene.getFlag( + "journal-to-canvas-slideshow", + "slideshowTiles" + )) || []; + + //check if the tile exists in the scene, and add a "missing" element if it does + if (shouldCheckIfExists) { + //get the ids of all the tiles in the scene + let sceneTileIDs = currentScene.tiles.map((tile) => tile.id); + + // if our tile data's id isn't included in the ids of tiles in the scene, add a missing property + flaggedTiles = flaggedTiles.map((tileData) => + !sceneTileIDs.includes(tileData.id) + ? { ...tileData, missing: true } + : tileData + ); + } + + if (type === "frame") { + return ArtTileManager.getFrameTiles(flaggedTiles); + } else if (type === "art") { + return ArtTileManager.getDisplayTiles(flaggedTiles); + } + + return flaggedTiles; + } + + /** + * Returns the "bounding" or "frame" tiles from the scene's displayTiles flag + * @param {Array} flaggedTiles - array of Display Tiles from a scene flag + * @returns filtered array with only the bounding tiles + */ + static getFrameTiles(flaggedTiles) { + return flaggedTiles.filter((tileData) => tileData.isBoundingTile); + } + /** + * Returns the Display tiles from the scene's displayTiles flag array + * @param {Array} flaggedTiles - array of SlideshowTiles from the scene's flag + * @returns filtered array with only the display tiles + */ + static getDisplayTiles(flaggedTiles) { + return flaggedTiles.filter((tileData) => !tileData.isBoundingTile); + } + static async renderTileConfig(tileID, sceneID = "") { + let tile = await game.scenes.viewed.getEmbeddedDocument("Tile", tileID); + if (tile) { + await tile.sheet.render(true); + } else { + ArtTileManager.displayTileNotFoundError(tileID); + } + } + + static async toggleTileZ(tileID, toFront = true) { + let tile = await game.scenes.viewed.getEmbeddedDocument("Tile", tileID); + if (!tile) return; + + const zIndex = tile.object.zIndex; + if (toFront) { + //save the default z-index + tile.defaultZ = zIndex; + await game.scenes.viewed.updateEmbeddedDocuments("Tile", { + _id: tileID, + object: { zIndex: 300 }, + }); + } else { + await game.scenes.viewed.updateEmbeddedDocuments("Tile", { + _id: tileID, + object: { zIndex: tile.defaultZ }, + }); + } + } + + static async selectTile(tileID, sceneID = "") { + let tile = await game.scenes.viewed.getEmbeddedDocument("Tile", tileID); + if (tile) { + await game.JTCS.utils.swapTools(); + tile.object.control({ + releaseOthers: true, + }); + } else { + ArtTileManager.displayTileNotFoundError(tileID); + } + } + + static async getLinkedFrameID(tileID, flaggedTiles) { + let tileData = await ArtTileManager.getTileDataFromFlag(tileID, flaggedTiles); + if (!tileData) { + console.error("Could not find tile data with that ID"); + } + return tileData.linkedBoundingTile; + } + /** + * Get the DisplayTile data + * @param {string} tileID - the id of the tile in scene we're looking to filter + * @param {Array} flaggedTiles - the flagged tiles + * @returns the flag data + */ + static async getTileDataFromFlag(tileID, flaggedTiles) { + let defaultData = ArtTileManager.getDefaultData(false, ""); + + if (!flaggedTiles) { + return defaultData; + } + + let flaggedTile = flaggedTiles.find((tileData) => tileData.id === tileID); + + //if we find a tile + if (flaggedTile) { + return flaggedTile; + } else { + return defaultData; + } + } + static async getTileObjectByID(tileID, sceneID = "") { + let ourScene = game.scenes.viewed; + if (sceneID) ourScene = game.scenes.get(sceneID); + let tile = await ourScene.getEmbeddedDocument("Tile", tileID); + if (tileID.includes === "new") { + // console.log("New tile created"); + } else { + if (!tile) { + ArtTileManager.displayTileNotFoundError(tileID); + } + } + return tile; + } + + static displayTileNotFoundError(tileID, sceneID = "") { + console.error("JTCS can't find tile in scene with ID " + tileID); + } + + /** + * Update a tile in this scene with new data, or create a new one + * @param {Object} displayData - the data we want to update with + * @param {String} tileID - the id of the tile we want to update + */ + static async updateSceneTileFlags(displayData, tileID) { + if (!tileID) { + return; + } + let currentScene = game.scenes.viewed; + let tiles = (await ArtTileManager.getSceneSlideshowTiles()) || []; + + if (tiles.find((tile) => tile.id === tileID)) { + tiles = tiles.map((tileData) => { + // if the ids match, update the matching one with the new displayName + return tileData.id === tileID + ? { ...tileData, ...displayData } + : tileData; //else just return the original + }); + } else { + tiles.push({ id: tileID, ...displayData }); + } + + await ArtTileManager.updateAllSceneTileFlags(tiles); + } + + /** + * Replace the slideshow tileData array stored in scene flags with the array passed in + * @param {Array} tiles - the tiles array we want to update our flag with + */ + static async updateAllSceneTileFlags(tiles, currentSceneID = "") { + let currentScene = game.scenes.get(currentSceneID); + if (!currentScene) currentScene = game.scenes.viewed; + await currentScene.setFlag( + "journal-to-canvas-slideshow", + "slideshowTiles", + tiles + ); + Hooks.callAll("updateArtGalleryTiles", { currentScene, updateData: tiles }); + } + static async deleteSceneTileData(tileID) { + let tiles = await ArtTileManager.getSceneSlideshowTiles(); + //filter out the tile that matches this + let deletedTileData = tiles.find((tileData) => tileData.id === tileID); + tiles = tiles.filter((tileData) => tileData.id !== tileID); + + if (deletedTileData.isBoundingTile) { + await ArtTileManager.updateLinkedArtTiles(tileID, "", tiles); + } + await ArtTileManager.updateAllSceneTileFlags(tiles); + + //call hook to delete the art tile data + } + + static async getAllScenesWithSlideshowData() { + let slideshowScenes = + game.scenes.contents.filter((scene) => { + if (game.version >= 10) + return scene.flags[`${MODULE_ID}`]?.slideshowTiles; + else return scene.data.flags[`${MODULE_ID}`]?.slideshowTiles; + }) || []; + return slideshowScenes; + } +} diff --git a/scripts/classes/CanvasIndicators.js b/scripts/classes/CanvasIndicators.js new file mode 100644 index 0000000..30069e6 --- /dev/null +++ b/scripts/classes/CanvasIndicators.js @@ -0,0 +1,160 @@ +import { ArtTileManager } from "./ArtTileManager.js"; +import { HelperFunctions } from "./HelperFunctions.js"; + +export class CanvasIndicators { + static async setUpIndicators(foundTileData, tileDoc) { + if (!tileDoc) { + ui.notifications.error("No tile doc was provided."); + return; + } + let type = "unlinked"; + + if (foundTileData) { + type = foundTileData.isBoundingTile ? "frame" : "art"; + const defaultTileID = await ArtTileManager.getDefaultArtTileID(); + if (foundTileData.id === defaultTileID) { + type = "default"; + } + } + await CanvasIndicators.createTileIndicator(tileDoc, type); + await CanvasIndicators.hideTileIndicator(tileDoc); + } + + static async getColors() { + let colors = {}; + let settingsColors = await HelperFunctions.getSettingValue( + "artGallerySettings", + "indicatorColorData.colors" + ); + colors.frameTileColor = settingsColors.frameTileColor || "#ff3300"; + colors.artTileColor = settingsColors.artTileColor || "#2f2190"; + colors.unlinkedTileColor = settingsColors.unlinkedTileColor || "#a2ff00"; + colors.defaultTileColor = settingsColors.defaultTileColor || "#e75eff"; + return colors; + } + + /** + * for v10, create an indicator that better reflects the image + * @author TheRipper93 (original author) + * @author Eva (added small changes better to fit module) + * https://github.com/theripper93/tile-sort/blob/master/scripts/main.js + * @returns - the created sprite + */ + static createV10Indicator(tile, fillAlpha, color) { + let tileImg = tile.mesh; + if (!tileImg || !tileImg.texture.baseTexture) return; + let sprite = new PIXI.Sprite.from(tileImg.texture); + sprite.isSprite = true; + sprite.width = tile.document.width; + sprite.height = tile.document.height; + sprite.angle = tileImg.angle; + sprite.alpha = fillAlpha; + sprite.tint = color; + sprite.name = "tilesorthighlight"; + return sprite; + } + static async createTileIndicator(tileDocument, type = "art") { + if (!tileDocument) { + ui.notifications.warn("Tile document not supplied."); + return; + } + //add check for if it's v10 + const isV10 = game.version >= 10 ? true : false; + let tileDimensions = { + width: isV10 ? tileDocument.width : tileDocument.data.width, + height: isV10 ? tileDocument.height : tileDocument.data.height, + x: isV10 ? tileDocument.x : tileDocument.data.x, + y: isV10 ? tileDocument.y : tileDocument.data.y, + }; + let tileObject = tileDocument.object; + if (!tileObject) { + return; + } + + if (tileObject && tileObject.overlayContainer) { + //destroy the overlayContainer PIXI Container stored on the tileObject + tileObject.overlayContainer.destroy(); + + //delete the property itself that was storing it + delete tileObject.overlayContainer; + } + let colors = await CanvasIndicators.getColors(); + let color; + let fillAlpha = 0.5; + let lineWidth; + switch (type) { + case "frame": + color = colors.frameTileColor; + lineWidth = 15; + break; + case "art": + color = colors.artTileColor; + lineWidth = 5; + break; + case "unlinked": + color = colors.unlinkedTileColor; + lineWidth = 15; + break; + case "default": + color = colors.defaultTileColor; + lineWidth = 15; + break; + } + color = color.substring(1); + if (color.length === 8) { + color = HelperFunctions.hex8To6(color); + } + color = `0x${color}`; + + tileObject.overlayContainer = tileObject.addChild(new PIXI.Container()); + + let overlayGraphic; + let overlaySprite; + if (game.version >= 10 && (type === "art" || type === "default")) { + overlaySprite = CanvasIndicators.createV10Indicator( + tileObject, + fillAlpha, + color + ); + tileObject.overlayContainer.addChild(overlaySprite); + } + overlayGraphic = new PIXI.Graphics(); + const whiteColor = 0xffffff; + fillAlpha = overlaySprite ? 0.25 : 0.5; + overlayGraphic.beginFill(whiteColor, fillAlpha); + overlayGraphic.lineStyle(lineWidth, color, 1); + overlayGraphic.tint = color; + + overlayGraphic.drawRect(0, 0, tileDimensions.width, tileDimensions.height); + overlayGraphic.endFill(); + + tileObject.overlayContainer.addChild(overlayGraphic); + tileObject.overlayContainer.alpha = 0; + } + + static async showTileIndicator(tileDocument, alpha = 1) { + if (!tileDocument || !tileDocument.object) { + console.warn("Tile document not supplied."); + return; + } + let tileObject = tileDocument.object; + if (tileObject.overlayContainer) { + tileObject.overlayContainer.alpha = alpha; + } else { + console.error("No overlay container found"); + } + } + static async hideTileIndicator(tileDocument) { + if (!tileDocument || !tileDocument.object) { + console.warn("No tile document supplied"); + return; + } + let tileObject = tileDocument.object; + + if (tileObject && tileObject.overlayContainer) { + tileObject.overlayContainer.alpha = 0; + } else { + console.error("No overlay container found"); + } + } +} diff --git a/scripts/classes/CustomDialog.js b/scripts/classes/CustomDialog.js new file mode 100644 index 0000000..8a33989 --- /dev/null +++ b/scripts/classes/CustomDialog.js @@ -0,0 +1 @@ +class CustomDialog extends Dialog {} diff --git a/scripts/classes/HelperFunctions.js b/scripts/classes/HelperFunctions.js new file mode 100644 index 0000000..4c16a5c --- /dev/null +++ b/scripts/classes/HelperFunctions.js @@ -0,0 +1,588 @@ +import { log, MODULE_ID } from "../debug-mode.js"; +import { artGalleryDefaultSettings } from "../settings.js"; + +export class HelperFunctions { + static MODULE_ID = "journal-to-canvas-slideshow"; + + /** + * pass in a string and capitalize each word in the string + * @param {String} string - the string whose words we want to capitalize + * @param {String} delimiter - a delimiter separating each word + * @returns A string with each word capitalized and the same delimiters + */ + static capitalizeEachWord(string, delimiter = " ") { + let sentenceArray; + let capitalizedString; + + if (!delimiter) { + // if the delimiter is an empty string, split it by capital letters, as if camelCase + sentenceArray = string.split(/(?=[A-Z])/).map((s) => s.toLowerCase()); + capitalizedString = sentenceArray + .map((s) => s.charAt(0).toUpperCase() + s.slice(1)) + .join(" "); + } else { + sentenceArray = string.split(delimiter); + + capitalizedString = sentenceArray + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(delimiter); + } + return capitalizedString; + } + static async resetArtGallerySettings() { + await HelperFunctions.setSettingValue("artGallerySettings", {}, "", false); + await HelperFunctions.setSettingValue( + "artGallerySettings", + artGalleryDefaultSettings, + "", + true + ); + // await game.settings.set(MODULE_ID, "artGallerySettings", newSettings); + } + static async swapTools(layerName = "background", tool = "select") { + if (game.version >= 10) { + if ((layerName = "background")) layerName = "tiles"; + } + ui.controls.controls.find((c) => c.layer === layerName).activeTool = tool; + + let ourLayer = game.canvas.layers.find((l) => l.options.name === layerName); + if (ourLayer) { + if (ourLayer && !ourLayer.active) { + ourLayer?.activate(); + } else { + ui.controls.render(true); + } + } else { + console.error("Can't find that layer", ourLayer); + } + } + /** + * Scale a tile's size, or one dimension (length or width) of a tile + * @param {Number} scale - the ratio to scale the tile by + * @param {String} axis - the tile's width or the tile's height or both + */ + static async scaleControlledTiles(scale = 0.5, axis = " ") { + const ourScene = game.scenes.viewed; + + const layerName = game.version >= 10 ? "tiles" : "background"; + const sceneTiles = canvas[layerName].controlled.filter( + (obj) => obj.document.documentName === "Tile" + ); + + let updateObjects = []; + + sceneTiles.forEach((tile) => { + let tileWidth = game.version >= 10 ? tile.width : tile.data.width; + let tileHeight = game.version >= 10 ? tile.height : tile.data.height; + let width = duplicate(tileWidth); + let height = duplicate(tileHeight); + height *= scale; + width *= scale; + updateObjects.push({ + _id: tile.id, + ...(axis === " " && { height: height, width: width }), + ...(axis === "height" && { height: height }), + ...(axis === "width" && { width: width }), + }); + }); + ourScene.updateEmbeddedDocuments("Tile", updateObjects); + } + + // Move tiles + // originally By @cole$9640 + //slightly edited by @Eva into a modular method + static async moveControlledTiles(amount = 10, axis = "x") { + const ourScene = game.scenes.viewed; + + let tiles; + if (game.version >= 10) { + tiles = canvas.tiles.controlled; + } else { + tiles = + canvas.background.controlled.length === 0 + ? canvas.foreground.controlled + : canvas.background.controlled; + } + if (tiles.length) { + const updates = tiles + .filter((tile) => { + if (game.version >= 10) return !tile.locked; + else return !tile.data.locked; + }) + .map((tile) => ({ + _id: tile.id, + [axis]: tile[axis] + amount, + })); + ourScene.updateEmbeddedDocuments("Tile", updates); + } else { + ui.notifications.notify("Please select at least one tile."); + } + } + + static async setFlagValue(document, flagName, updateData, nestedKey = "") { + await document.setFlag(MODULE_ID, flagName, updateData); + } + /** + * Get the value of a document's flag + * @param {Object} document - the document whose flags we want to set (Scene, Actor, Item, etc) + * @param {String} flagName - the name of the flag + * @param {String} nestedKey - a string of nested properties separated by dot notation that we want to set + * @param {*} returnIfEmpty - a value to return if the flag is undefined + * @returns + */ + static async getFlagValue(document, flagName, nestedKey = "", returnIfEmpty = []) { + let flagData = await document.getFlag(MODULE_ID, flagName); + if (!flagData) { + flagData = returnIfEmpty; + } + return flagData; + } + + /** + * Sets a value, using the "flattenObject" and "expandObject" utilities to reach a nested property + * @param {String} settingName - the name of the setting + * @param {*} updateData - the value you want to set a property to + * @param {String} nestedKey - a string of dot-separated values to refer to a nested property + */ + static async setSettingValue( + settingName, + updateData, + nestedKey = "", + isFormData = false + ) { + if (isFormData) { + let currentSettingData = game.settings.get( + HelperFunctions.MODULE_ID, + settingName + ); + updateData = expandObject(updateData); //get expanded object version of formdata keys, which were strings in dot notation previously + updateData = mergeObject(currentSettingData, updateData); + // let updated = await game.settings.set(HelperFunctions.MODULE_ID, settingName, currentSettingData); + // console.warn(updated); + } + if (nestedKey) { + let settingData = game.settings.get(HelperFunctions.MODULE_ID, settingName); + setProperty(settingData, nestedKey, updateData); + await game.settings.set(HelperFunctions.MODULE_ID, settingName, settingData); + } else { + await game.settings.set(HelperFunctions.MODULE_ID, settingName, updateData); + } + } + + /* --------------------------------- Colors --------------------------------- */ + /** + * + * Get the contrasting color for any hex color + * (c) 2019 Chris Ferdinandi, MIT License, https://gomakethings.com + * Derived from work by Brian Suda, https://24ways.org/2010/calculating-color-contrast/ + * @param {String} A hexcolor value + * @return {String} The contrasting color (black or white) + **/ + static getContrast(hexcolor) { + // If a leading # is provided, remove it + if (hexcolor.slice(0, 1) === "#") { + hexcolor = hexcolor.slice(1); + } + + // If a three-character hexcode, make six-character + if (hexcolor.length === 3) { + hexcolor = hexcolor + .split("") + .map(function (hex) { + return hex + hex; + }) + .join(""); + } + + // Convert to RGB value + var r = parseInt(hexcolor.substr(0, 2), 16); + var g = parseInt(hexcolor.substr(2, 2), 16); + var b = parseInt(hexcolor.substr(4, 2), 16); + + // Get YIQ ratio + var yiq = (r * 299 + g * 587 + b * 114) / 1000; + + // Check contrast + //@author: slightly modified this + return yiq; // >= 128 ? "black" : "white"; + // return yiq >= 128 ? "black" : "white"; + } + /** + * Will return 1 if color is darker than gray, -1 if color is lighter than gray + * @param {String} hexColor - string hex color + * @returns {Number} -1 or +1 + */ + static lighterOrDarker(hexColor) { + const HF = HelperFunctions; + let yiq = HF.getContrast(hexColor); + //the 128 is like a value between 0 and 255, so gray + //checking to see if the contrast value is greater than gray (black) or less than gray (white) + return yiq >= 128 ? -1 : 1; + } + /** + * + * @param {String} backgroundColor - our background color + * @param {String} accentColor - the color we're adjusting to find a tint with better contrast + * @returns a color lightened to have the right contrast + */ + static getColorWithContrast(backgroundColor, accentColor) { + const HF = HelperFunctions; + + backgroundColor = HF.hex8To6(backgroundColor); + accentColor = HF.hex8To6(accentColor); + + const contrastValue = HF.getContrastBetween(backgroundColor, accentColor); + const direction = contrastValue > 0 ? 1 : -1; + // const text = contrastValue < 0 ? "We should darken color" : "we should lighten color"; + let adjustedColor = HF.hex8To6(accentColor); + + for ( + let adjustAmount = 0, times = 0; + times < 15; + adjustAmount += direction * 10, times += 1 + ) { + adjustedColor = HF.LightenDarkenColor(accentColor, adjustAmount); + + const hasEnoughContrast = HF.checkIfColorsContrastEnough( + backgroundColor, + adjustedColor + ); + if (hasEnoughContrast) { + break; + } + } + + return adjustedColor; + } + + static getContrastBetween(backgroundColor, accentColor) { + const HF = HelperFunctions; + let contrast1 = HF.getContrast(backgroundColor); + let contrast2 = HF.getContrast(accentColor); + //the 128 is like a value between 0 and 255, so gray. + //if luminance? is grater than 128, it's between gray and white, so return a dark color + //if luminance? is less than 128, it's between black and gray, so return a light color + let contrastBetween = contrast2 - contrast1; + return contrastBetween; + } + + static checkIfColorsContrastEnough(hexColor1, hexColor2) { + const HF = HelperFunctions; + let contrast1 = HF.getContrast(hexColor1); + let contrast2 = HF.getContrast(hexColor2); + //the 128 is like a value between 0 and 255, so gray. + //if luminance? is grater than 128, it's between gray and white, so return a dark color + //if luminance? is less than 128, it's between black and gray, so return a light color + let contrastBetween = Math.abs(contrast2 - contrast1); + return contrastBetween >= 128 ? true : false; + } + + /** + * Programmatically lighten or darken a color + * @author "Pimp Trizkit" on Stackoverflow + * @link https://stackoverflow.com/questions/5560248/programmatically-lighten-or-darken-a-hex-color-or-rgb-and-blend-colors + * @returns a lightened or darkened color + */ + static LightenDarkenColor(col, amt) { + var usePound = false; + if (col[0] == "#") { + col = col.slice(1); + usePound = true; + } + var num = parseInt(col, 16); + + var r = (num >> 16) + amt; + + if (r > 255) r = 255; + else if (r < 0) r = 0; + + var b = ((num >> 8) & 0x00ff) + amt; + + if (b > 255) b = 255; + else if (b < 0) b = 0; + + var g = (num & 0x0000ff) + amt; + + if (g > 255) g = 255; + else if (g < 0) g = 0; + var string = "000000" + (g | (b << 8) | (r << 16)).toString(16); + return (usePound ? "#" : "") + string.substring(string.length - 6); + // return (usePound ? "#" : "") + (g | (b << 8) | (r << 16)).toString(16); + } + + /** + * set the custom colors for the indicators and color scheme in the JTCS Apps + * @param {String} settingPropertyString - the string name of the property in the settings + */ + static async getColorDataFromSettings(settingPropertyString) { + const HF = HelperFunctions; + let colorData = await HelperFunctions.getSettingValue( + "artGallerySettings", + settingPropertyString + ); + let { colors, propertyNames, colorVariations } = colorData; + + Object.keys(colors).forEach((colorKey) => { + const value = HF.hex8To6(colors[colorKey]); + const propertyName = propertyNames[colorKey]; + let shouldMakeVariants = false; + if (colorVariations) { + shouldMakeVariants = colorVariations[colorKey]; + } + HF.setRootStyleProperty(propertyName, value, shouldMakeVariants); + }); + + // add these extra bits on for now + if (settingPropertyString === "colorSchemeData") { + let { accentColor, backgroundColor } = colors; + + // accentColor = accentColor;//HF.getColorWithContrast(backgroundColor, accentColor); + backgroundColor = HF.hex8To6(backgroundColor); + + const colorNeutral = + HF.getContrast(backgroundColor) >= 128 ? "black" : "white"; + const textColor = HF.getContrast(accentColor) >= 128 ? "black" : "white"; + + HF.setRootStyleProperty("--JTCS-text-color-on-bg", colorNeutral); //for text on the background color + HF.setRootStyleProperty("--JTCS-text-color-on-fill", textColor); //for text on buttons and filled labels + + HF.setRootStyleProperty("--JTCS-accent-color", accentColor); //HF.getColorWithContrast(backgroundColor, accentColor)); + } + } + /** + * set the custom colors for the indicators and color scheme in the JTCS Apps + */ + static async setUIColors() { + await this.getColorDataFromSettings("indicatorColorData"); + await this.getColorDataFromSettings("colorSchemeData"); + } + + /** + * Set properties of custom colors on root element for use in our CSS + * @param {String} propertyName - the name of the CSS custom property we want to set on the root element + * @param {String} value - a value representing a hex code color + * @param {*} makeVariations - whether we should generate light and dark variants on this color for better contrast + */ + static setRootStyleProperty(propertyName, value, makeVariations = false) { + const html = document.documentElement; + const HF = HelperFunctions; + value = HF.hex8To6(value); + + html.style.setProperty(propertyName, value); + + if (makeVariations) { + const direction = HF.lighterOrDarker(value); + const shouldDarken = direction < 0 ? true : false; + const text = + direction < 0 ? "We should darken color" : "we should lighten color"; + + let startNumber = !shouldDarken ? 80 : 0; + let step = !shouldDarken ? -10 : 10; + + for (var number = startNumber; Math.abs(number) < 90; number += step) { + const variantPropName = `${propertyName}-${number + .toString() + .padStart(2, "0")}`; + // const amount = number; + const amount = shouldDarken ? number * -1 : number; + const variantValue = HF.LightenDarkenColor(value, amount); + html.style.setProperty(variantPropName, variantValue); + } + if (propertyName.includes("background-color")) { + const htmlStyle = getComputedStyle(html); + let inputBG = htmlStyle.getPropertyValue("--JTCS-background-color"); + let elevationBG = htmlStyle.getPropertyValue("--JTCS-background-color"); + let borderColor = htmlStyle.getPropertyValue( + "--JTCS-background-color-70" + ); + let shadowColor = htmlStyle.getPropertyValue( + "--JTCS-background-color-50" + ); + let dangerColor = htmlStyle.getPropertyValue("--color-danger-base"); + let warningColor = htmlStyle.getPropertyValue("--color-warning-base"); + let infoColor = htmlStyle.getPropertyValue("--color-info-base"); + let successColor = htmlStyle.getPropertyValue("--color-success-base"); + let tileItemColor = "transparent"; + if (!shouldDarken) { + inputBG = getComputedStyle(html).getPropertyValue( + "--JTCS-background-color-20" + ); + borderColor = "transparent"; //getComputedStyle(html).getPropertyValue("--JTCS-background-color"); + shadowColor = "transparent"; + elevationBG = htmlStyle.getPropertyValue( + "--JTCS-background-color-10" + ); + dangerColor = htmlStyle.getPropertyValue("--color-danger-light"); + dangerColor = htmlStyle.getPropertyValue("--color-danger-light"); + infoColor = htmlStyle.getPropertyValue("--color-info-light"); + successColor = htmlStyle.getPropertyValue("--color-success-light"); + tileItemColor = elevationBG; + } + html.style.setProperty("--JTCS-box-shadow-color", shadowColor); + html.style.setProperty("--JTCS-input-background-color", inputBG); + html.style.setProperty("--JTCS-border-color", borderColor); + html.style.setProperty("--JTCS-elevation-BG-color", elevationBG); + html.style.setProperty("--JTCS-danger-color", dangerColor); + html.style.setProperty("--JTCS-warning-color", warningColor); + html.style.setProperty("--JTCS-info-color", dangerColor); + html.style.setProperty("--JTCS-success-color", warningColor); + html.style.setProperty("--JTCS-tile-item-bg-color", tileItemColor); + } + } + } + + /** + * Checks if color is in hex8 format, and if so slices string to make it hex6 + * @param {String} hexColor - a hex color code with "#" up front + * @returns {String} + */ + static hex8To6(hexColor) { + let hexColorMod = hexColor; + if (hexColor.slice(1).length > 6) { + hexColorMod = hexColor.slice(0, -2); + } + return hexColorMod; + } + static async getSettingValue(settingName, nestedKey = "") { + let settingData = await game.settings.get(HelperFunctions.MODULE_ID, settingName); + if (settingData !== undefined && settingData !== null) { + if (nestedKey) { + let nestedSettingData = getProperty(settingData, nestedKey); + + return nestedSettingData; + } + return settingData; + } else { + console.error("Cannot find setting with name " + settingName); + } + } + + static async checkSettingEquals(settingName, compareToValue) { + if (game.settings.get(HelperFunctions.MODULE_ID, settingName) == compareToValue) { + return true; + } + return false; + } + + static async showWelcomeMessage() { + let options = {}; + let d = new Dialog({ + title: "Welcome Message", + content: `

+

Journal To Canvas Slideshow Has Updated

+

Journal to Canvas Slideshow is now known as "JTCS - Art Gallery"

+

The module has received a huge overhaul, updates and improvements to preexisting features, and the addition of brand new features. +

+ +
    +
  1. + + View a detailed guide and walkthrough of the new features here + +
  2. +
  3. ReadMe and Feature List
  4. +
  5. Release Notes
  6. +
+

+

Note: This welcome message can be turned on and off in the module settings, but will be enabled after updates to inform you of important changes.

+
`, + buttons: { + disable: { + label: "Disable Welcome Message", + callback: async () => await HelperFunctions.disableWelcomeMessage(), + }, + continue: { + label: "Continue without Disabling", + }, + }, + }); + d.render(true); + } + + static async disableWelcomeMessage() { + //disable the welcome message + await HelperFunctions.setSettingValue("showWelcomeMessage", false); + } + static isImage(url) { + return /\.(jpg|jpeg|png|webp|avif|gif|svg)$/.test(url); + } + /** + * Validating text input + * @param {String} inputValue - the input value + * @param {string} validationType - the type of input, what are we looking for, an image, video, etc. + * @param {function} onInvalid - callback function for if our input is invalid + * @returns true or false depending on if our input is valid or not + */ + + static validateInput(inputValue, validationType, onInvalid = "") { + let valid = false; + switch (validationType) { + case "image": + valid = HelperFunctions.isImage(inputValue); + break; + default: + valid = inputValue !== undefined; + break; + } + return valid; + } + + static getElementPositionAndDimension(element) { + return {}; + } + + static isOutsideClick(event) { + if ($(event.target).closest(".popover").length) { + //click was on the popover + return false; + } + //if our click is outside of our popover element + return true; + } + + /** + * Create a dialog + * @param {String} title - the title of the dialog + * @param {String} templatePath - the path to the template being used for this dialog + * @param {Object} data - the data object + * @param {Object} data.buttons - the buttons at the bottom of the prompt + */ + static async createDialog(title, templatePath, data) { + const options = { + width: 600, + // height: 250, + id: "JTCS-custom-dialog", + }; + let renderedHTML = await renderTemplate(templatePath, data); + let d = new Dialog( + { + title: title, + content: renderedHTML, + buttons: data.buttons, + }, + options + ).render(true); + } + static async createEventActionObject( + name, + callback, + shouldRenderAppOnAction = false + ) { + return { + name: name, + callback: callback, + shouldRenderAppOnAction: shouldRenderAppOnAction, + }; + } + + static editorsActive(sheet) { + let hasActiveEditors = Object.values(sheet.editors).some( + (editor) => editor.active + ); + return hasActiveEditors; + } + /// +} diff --git a/scripts/classes/ImageDisplayManager.js b/scripts/classes/ImageDisplayManager.js new file mode 100644 index 0000000..6aad930 --- /dev/null +++ b/scripts/classes/ImageDisplayManager.js @@ -0,0 +1,469 @@ +import { ArtTileManager } from "./ArtTileManager.js"; +import { HelperFunctions } from "./HelperFunctions.js"; +import ImageVideoPopout from "./MultiMediaPopout.js"; +/** + * This class manages the images specifically, setting and clearing the tiles' images + */ +export class ImageDisplayManager { + static async getTilesFromArtScene() { + let artSceneID = await game.JTCS.utils.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.scene.value" + ); + let ourScene = game.scenes.get(artSceneID); + let artTiles = await game.JTCS.tileUtils.getSceneSlideshowTiles("art", true, { + currentSceneID: artSceneID, + }); + let artTileID = artTiles[0].id; + + let frameTiles = await game.JTCS.tileUtils.getSceneSlideshowTiles("frame", true, { + currentSceneID: artSceneID, + }); + let frameTileID = artTiles[0].linkedBoundingTile || frameTiles[0].id; + + return { + ourScene: ourScene, + artTileID: artTileID, + frameTileID: frameTileID, + }; + } + + static async updateTileObjectTexture( + artTileID, + frameTileID, + url, + method, + sceneID = "" + ) { + let ourScene = game.scenes.get(sceneID); + if (!ourScene) ourScene = game.scenes.viewed; + + let artTile = ourScene.tiles.get(artTileID); + let frameTile = ourScene.tiles.get(frameTileID); + + //load the texture from the source + if (!artTile || !url) { + ui.notifications.error("Tile or image not found"); + console.error(url, artTile, artTileID); + return; + } + const tex = await loadTexture(url); + + if (!tex) { + ui.notifications.error( + `Error loading texture from '${url}'. Access to URL likely blocked by CORS policy.` + ); + return; + } + let imageUpdate; + + if (!frameTile) { + imageUpdate = await ImageDisplayManager.scaleArtTileToScene( + artTile, + tex, + url, + sceneID + ); + } else { + imageUpdate = await ImageDisplayManager.scaleArtTileToFrameTile( + artTile, + frameTile, + tex, + url, + sceneID + ); + } + + const updated = await ourScene + .updateEmbeddedDocuments("Tile", [imageUpdate]) + .catch((error) => + ui.notifications.error( + `Default art tile in ${ourScene.name} couldn't be updated` + ) + ); + if (updated && method === "artScene") { + const { autoActivate, autoView } = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.scene" + ); + + if (autoActivate) { + ourScene.activate(); + } + if (autoView) { + ourScene.view(); + } + if ( + game.user.isGM && //if we're GM + ((!autoActivate && !autoView) || (ourScene.active && !ourScene.viewed)) //if the scene is neither set to activate or to view, notify that the image updated. + // if the scene is active but not viewed, notify that the image updated + ) { + ui.notifications.info( + `Default Tile in Art Scene '${ourScene.name}' successfully updated` + ); + } + } + } + + static async scaleArtTileToScene(displayTile, tex, url, sceneID = "") { + let displayScene = game.scenes.get(sceneID); + if (!displayScene) displayScene = game.scenes.viewed; + + let displaySceneWidth = + game.version >= 10 ? displayScene.width : displayScene.data.width; + let displaySceneHeight = + game.version >= 10 ? displayScene.height : displayScene.data.height; + let dimensionObject = await ImageDisplayManager.calculateAspectRatioFit( + tex.width, + tex.height, + displaySceneWidth, + displaySceneHeight + // displayScene.data.width, + // displayScene.data.height + ); + //scale down factor is how big the tile will be in the scene + //make this scale down factor configurable at some point + let scaleDownFactor = 200; + dimensionObject.width -= scaleDownFactor; + dimensionObject.height -= scaleDownFactor; + //half of the scene's width or height is the center -- we're subtracting by half of the image's width or height to account for the offset because it's measuring from top/left instead of center + //separate objects depending on the texture's dimensions -- + //create an 'update' object for if the image is wide (width is bigger than height) + let wideImageUpdate = { + _id: displayTile.id, + width: dimensionObject.width, + height: dimensionObject.height, + img: url, + x: scaleDownFactor / 2, + y: displaySceneHeight / 2 - dimensionObject.height / 2, + }; + //create an 'update' object for if the image is tall (height is bigger than width) + let tallImageUpdate = { + _id: displayTile.id, + width: dimensionObject.width, + height: dimensionObject.height, + img: url, + y: scaleDownFactor / 2, + x: displaySceneWidth / 2 - dimensionObject.width / 2, + }; + //https://stackoverflow.com/questions/38675447/how-do-i-get-the-center-of-an-image-in-javascript + //^used the above StackOverflow post to help me figure that out + //Determine if the image or video is wide, tall, or same dimensions and update depending on that + + if (dimensionObject.height > dimensionObject.width) { + //if the height is longer than the width, use the tall image object + return tallImageUpdate; + } else if (dimensionObject.width > dimensionObject.height) { + //if the width is longer than the height, use the wide image object + return wideImageUpdate; + } + + //if the image length and width are pretty much the same, just default to the wide image update object + return wideImageUpdate; + } + + static async scaleArtTileToFrameTile(artTile, frameTile, tex, url) { + const frameTileWidth = + game.version >= 10 ? frameTile.width : frameTile.data.width; + const frameTileHeight = + game.version >= 10 ? frameTile.height : frameTile.data.height; + const frameTileX = game.version >= 10 ? frameTile.x : frameTile.data.x; + const frameTileY = game.version >= 10 ? frameTile.y : frameTile.data.y; + + let dimensionObject = ImageDisplayManager.calculateAspectRatioFit( + tex.width, + tex.height, + frameTileWidth, + frameTileHeight + ); + + let imageUpdate = { + _id: artTile.id, + width: dimensionObject.width, + height: dimensionObject.height, + img: url, + y: frameTileY, + x: frameTileX, + }; + //Ensure image is centered to bounding tile (stops images hugging the top left corner of the bounding box). + let boundingMiddle = { + x: frameTileX + frameTileWidth / 2, + y: frameTileY + frameTileHeight / 2, + }; + + let imageMiddle = { + x: imageUpdate.x + imageUpdate.width / 2, + y: imageUpdate.y + imageUpdate.height / 2, + }; + + imageUpdate.x += boundingMiddle.x - imageMiddle.x; + imageUpdate.y += boundingMiddle.y - imageMiddle.y; + return imageUpdate; + } + /** Used snippet from the below stackOverflow answer to help me with proportionally resizing the images*/ + /*https://stackoverflow.com/questions/3971841/how-to-resize-images-proportionally-keeping-the-aspect-ratio*/ + static calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) { + let ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight); + return { + width: srcWidth * ratio, + height: srcHeight * ratio, + }; + } + static async displayImageInWindow(method, url) { + if (method === "journalEntry") { + let dedicatedDisplayData = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData" + ); + const displayJournalID = dedicatedDisplayData.journal.value; + const displayJournal = game.journal.get(displayJournalID); + //if we would like to display in a dedicated journal entry + if (!displayJournal) { + //couldn't find display journal, so return + if (game.user.isGM) { + ui.notifications.error( + `No Art Journal entry set! Please set your art journal in the module settings or Art Gallery Config` + ); + } + return; + } else { + displayJournal.render(true); + } + + //get the file type of the image url via regex match + var fileTypePattern = /\.[0-9a-z]+$/i; + var fileType = url.match(fileTypePattern); + + let journalMode = "image"; + let update; + + if (fileType == ".mp4" || fileType == ".webm") { + if (game.version < 10) { + // if the file type is a video and we're before v10, we have to do a bit of a wonky workaround + let videoHTML = `
+ +
+ `; + + update = { + _id: displayJournal._id, + content: videoHTML, + img: "", + }; + imageMode = "text"; + } else { + //if we're after v10 + update = { + _id: displayJournal.id, + src: url, + video: { + loop: true, + autoplay: true, + }, + type: "video", + }; + } + } else { + //change the background image to be the clicked image in the journal + if (game.version < 10) { + update = { + _id: displayJournal.id, + content: "", + img: url, + }; + } else { + update = { + // _id: displayJournal.id, + src: url, + type: "image", + }; + } + } + let updated; + if (game.version < 10) { + updated = await displayJournal.update(update, {}); + } else { + const firstPage = displayJournal.pages.contents[0]; + updated = await firstPage?.update({ _id: firstPage.id, ...update }); + } + if (updated === null && game.user.isGM) { + ui.notifications.error( + `Could not display image in Art Journal '${displayJournal.name}.'` + ); + } + const { autoActivate, autoView } = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.journal" + ); + + if (autoActivate) displayJournal.show(journalMode, true); + if (autoView && !displayJournal.sheet.rendered) + displayJournal.sheet.render(true); + + if (game.user.isGM && !autoActivate && !autoView) { + ui.notifications.info( + `Image in Art Journal '${displayJournal.name}' successfully updated` + ); + } + } else if (method === "window") { + //if we would like to display in a new popout window + let popout = new ImageVideoPopout(url, { + shareable: true, + }) + .render(true) + .shareImage(); + } + } + + /** + * determine the location of the display + * @param {*} imageElement - the imageElement + * @param {*} location - the location we want to display our image in + * @param {*} journalSheet - the journal sheet in which we're performing these actions + * @param {*} url + */ + static async determineDisplayMethod(sheetImageData = { method: "window", url: "" }) { + let { method, imageElement, url } = sheetImageData; + if (!url && imageElement) { + url = ImageDisplayManager.getImageSource(imageElement); + } else if (!url && !imageElement) { + console.error("No image data passed to this method", url, imageElement); + } + //on click, this method will determine if the image should open in a scene or in a display journal + switch (method) { + case "artScene": + let artSceneID = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.scene.value" + ); + let artScene = game.scenes.get(artSceneID); + if (artScene) { + let defaultArtTileID = await ArtTileManager.getDefaultArtTileID( + artScene + ); + if (!defaultArtTileID) { + ui.notifications.error( + "No valid 'Art Tile' found in scene '" + artScene.name + "'" + ); + return; + } + let frameTileID = await ArtTileManager.getGalleryTileDataFromID( + defaultArtTileID, + "linkedBoundingTile", + artSceneID + ); + await ImageDisplayManager.updateTileObjectTexture( + defaultArtTileID, + frameTileID, + url, + method, + artSceneID + ); + } + break; + case "anyScene": + let { artTileID, frameTileID } = sheetImageData; + //if the setting is to display it in a scene, proceed as normal + + if (method === "anyScene" && !artTileID) { + artTileID = await ArtTileManager.getDefaultArtTileID( + game.scenes.viewed + ); + if (!artTileID) { + ui.notifications.error( + "No valid 'Art Tile' found in current scene" + ); + return; + } + frameTileID = await ArtTileManager.getGalleryTileDataFromID( + artTileID, + "linkedBoundingTile" + ); + } + await ImageDisplayManager.updateTileObjectTexture( + artTileID, + frameTileID, + url, + method + ); + break; + case "journalEntry": + case "window": + await ImageDisplayManager.displayImageInWindow(method, url); + break; + } + } + + static getImageSource(imageElement) { + let type = imageElement.nodeName; + let url; + + if (type == "IMG") { + //if it's an image element + url = imageElement.getAttribute("src"); + } else if (type == "VIDEO") { + //if it's a video element + url = imageElement.getElementsByTagName("source")[0]?.getAttribute("src"); + if (!url) { + url = imageElement.getAttribute("src"); + } + } else if (type == "DIV" && imageElement.classList.contains("lightbox-image")) { + //if it's a lightbox image on an image-mode journal + //https://stackoverflow.com/questions/14013131/how-to-get-background-image-url-of-an-element-using-javascript -- + let imgStyle = imageElement.style; + url = imgStyle.backgroundImage.slice(4, -1).replace(/['"]/g, ""); + } else { + ui.notifications.error("Type not supported"); + url = null; + } + if (!url) { + ui.notifications.error("url not found"); + } + return url; + + //load the texture from the source + } + + static async clearDisplayWindow() { + if (!findDisplayJournal()) { + return; + } + let url = + "/modules/journal-to-canvas-slideshow/artwork/HD_transparent_picture.png"; + let update = { + _id: displayJournal.id, + img: url, + content: `
`, + }; + + const updated = await displayJournal.update(update, {}); + } + + static async clearTile(tileID, options = {}) { + let { ourScene } = options; + if (!ourScene) ourScene = game.scenes.viewed; + const isBoundingTile = await ArtTileManager.getGalleryTileDataFromID( + tileID, + "isBoundingTile" + ); + + let propertyPath = "defaultTileImages.paths.artTilePath"; + if (isBoundingTile) { + propertyPath = "defaultTileImages.paths.frameTilePath"; + } + + const clearImagePath = await HelperFunctions.getSettingValue( + "artGallerySettings", + propertyPath + ); + + var clearTileUpdate = { + _id: tileID, + img: clearImagePath, + }; + await ourScene.updateEmbeddedDocuments("Tile", [clearTileUpdate]); + } +} diff --git a/scripts/classes/JTCSSettingsApplication.js b/scripts/classes/JTCSSettingsApplication.js new file mode 100644 index 0000000..440f2f3 --- /dev/null +++ b/scripts/classes/JTCSSettingsApplication.js @@ -0,0 +1,321 @@ +import { log, MODULE_ID } from "../debug-mode.js"; +import { artGalleryDefaultSettings, colorThemes } from "../settings.js"; +import { universalInterfaceActions as UIA } from "../data/Universal-Actions.js"; +import { HelperFunctions, HelperFunctions as HF } from "./HelperFunctions.js"; +import { ArtTileManager } from "./ArtTileManager.js"; + +const settingsActions = { + global: { + resetColors: { + onClick: () => {}, + }, + }, + item: {}, +}; +/** + * Form app to handle JTCS settings + */ +export class JTCSSettingsApplication extends FormApplication { + constructor(data = {}) { + super(); + // this.data = data; + this.data = game.JTCS.utils.getSettingValue("artGallerySettings"); + } + + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["form"], + width: 600, + popOut: true, + resizable: true, + minimizable: true, + submitOnClose: false, + closeOnSubmit: true, + submitOnChange: false, + template: `modules/${MODULE_ID}/templates/JTCS-settings-app.hbs`, + id: "JTCSSettingsApplication", + title: " JTCSSettings Application", + scrollY: [".form-content"], + onsubmit: (event) => {}, + }); + } + + async addArtSceneAndJournalData(dedicatedDisplayData) { + const { journal, scene } = dedicatedDisplayData; + function mapNameAndId(contents) { + const newData = {}; + contents.forEach((c) => { + newData[c.id] = c.name; + }); + return newData; + } + const allJournals = mapNameAndId(game.journal.contents); + + const artJournalID = journal.value; + + const artJournalData = { + options: allJournals, + value: artJournalID, + }; + + const allScenes = mapNameAndId( + await ArtTileManager.getAllScenesWithSlideshowData() + ); + const artSceneID = scene.value; + + const artSceneData = { + options: allScenes, + value: artSceneID, + }; + return { + ...dedicatedDisplayData, + journal: { + ...journal, + artJournalData, + }, + scene: { + ...scene, + artSceneData, + }, + }; + } + + async getData() { + // Send data to the template + // let data = game.JTCS.utils.getSettingValue("artGallerySettings"); + this.data = await game.JTCS.utils.getSettingValue("artGallerySettings"); + let data = { ...this.data }; + let newDisplayData = await this.addArtSceneAndJournalData( + data.dedicatedDisplayData + ); + setProperty(data, "dedicatedDisplayData", newDisplayData); + return data; + } + + activateListeners(html) { + super.activateListeners(html); + + html.off("click").on( + "click", + "[data-action]", + this._handleButtonClick.bind(this) + ); + this._handleChange(); + } + + async handleAction(event, actionType) {} + + async _handleChange() { + $(`select, input[type='checkbox'], input[type='radio'], input[type='text']`).on( + "change", + async function (event) { + let { value, name, checked, type } = event.currentTarget; + let propertyString = ""; + switch (type) { + case "select": + case "radio": + //the radio or select data is nested in a parent object that holds both its choices and the chosen value + //so we will want to get that parent object instead of the object itself + propertyString = name.split(".").splice(0, 2).join("."); + break; + case "checkbox": + value = checked; //if its a checkbox, set its value to whether or not it is checked + break; + case "text": + propertyString = name.split(".").splice(0, 1).join("."); + break; + default: + propertyString = name; + break; + } + let settingsObject = getProperty( + artGalleryDefaultSettings, + propertyString + ); + if (settingsObject && settingsObject.hasOwnProperty("onChange")) { + let ourApp = game.JTCSSettingsApp; + settingsObject.onChange(event, { + value: value, + app: ourApp, + html: ourApp.element, + }); + } + } + ); + } + + async _handleButtonClick(event) { + let clickedElement = $(event.currentTarget); + let action = clickedElement.data().action; + let type = clickedElement[0].type; + if (type === "submit") { + this.element.find("form").on("submit", (e) => { + if (action === "closeOnSubmit") { + this.close(); + } + }); + + return; + } + event.stopPropagation(); + event.preventDefault(); + + let backgroundColor = this.element.find("#backgroundColor").val(); + let outerWrapper = clickedElement.closest(".outer-wrapper"); + + const accentElement = outerWrapper.find("[data-responsive-color]"); + let accentColor = accentElement.val(); + + let contrastNotification = outerWrapper.find(".inline-notification"); + + if (action === "checkContrast") { + let hasEnoughContrast = HF.checkIfColorsContrastEnough( + backgroundColor, + accentColor + ); + if (!hasEnoughContrast && contrastNotification.length === 0) { + await UIA.renderInlineNotification(event, "outer-wrapper", { + message: + "This color does not have enough contrast with the background color you've chosen. Text and buttons might be unreadable.", + notificationType: "warning", + }); + } else if (hasEnoughContrast && contrastNotification.length > 0) { + contrastNotification.remove(); + } + } else if (action === "toggleAutoContrastOff") { + const templatePath = game.JTCS.templates["delete-confirmation-prompt"]; + const buttons = { + cancel: { + label: "Cancel", + icon: "", + }, + delete: { + label: "Turn Off Auto-Contrast", + icon: "", + callback: async () => { + await HF.setSettingValue( + "artGallerySettings", + false, + "colorSchemeData.autoContrast" + ); + this.render(true); + }, + }, + }; + const data = { + icon: "fas fa-exclamation", + heading: "Turn off Auto-Contrast?", + destructiveActionText: `Turn off auto contrast`, + explanation: `This will make it so your chosen colors aren't automatically adjusted to contrast with your background color; +
However you risk choosing colors that make text illegible`, + buttons, + }; + await HF.createDialog("Turn Off Auto Contrast", templatePath, data); + } else if (action === "toggleAutoContrastOn") { + await HF.setSettingValue( + "artGallerySettings", + true, + "colorSchemeData.autoContrast" + ); + this.render(true); + } else if (action === "resetColor") { + let key = accentElement.attr("name"); + let theme = await HF.getSettingValue( + "artGallerySettings", + "colorSchemeData.theme" + ); + // const newScheme = mergeObject(currentSettings, colorThemes[theme]); + const defaultValue = getProperty(colorThemes[theme], key); + await HF.setSettingValue("artGallerySettings", defaultValue, key); + this.render(true); + // accentElement.val(defaultValue); + } else if (action === "scrollTo") { + const parentItem = clickedElement.closest("form"); + UIA.scrollOtherElementIntoView(event, { parentItem }); + } else if (action === "applyChanges") { + const form = event.currentTarget.closest("form"); + const formData = {}; + Array.from(form.querySelectorAll("input, select")).forEach((input) => { + let value = input.value; + if (input.type === "checkbox") { + value = input.checked; + } + formData[input.name] = value; + }); + await HF.setSettingValue("artGallerySettings", formData, "", true); + this.render(true); + } else if (action === "setDarkTheme" || action === "setLightTheme") { + const theme = action === "setDarkTheme" ? "dark" : "light"; + const templatePath = game.JTCS.templates["delete-confirmation-prompt"]; + const buttons = { + cancel: { + label: "Cancel", + icon: "", + }, + reset: { + label: `Apply Default ${HF.capitalizeEachWord(theme)} Theme`, + icon: "", + callback: async () => { + //store the chosen theme as a setting + await HF.setSettingValue( + "artGallerySettings", + theme, + "colorSchemeData.theme" + ); + const currentSettings = await HF.getSettingValue( + "artGallerySettings" + ); + + const newScheme = mergeObject( + currentSettings, + colorThemes[theme] + ); + await HF.setSettingValue("artGallerySettings", newScheme); + this.render(true); + }, + }, + }; + const data = { + icon: "fas fa-exclamation", + heading: `Apply Default ${HF.capitalizeEachWord(theme)} Theme?`, + destructiveActionText: `Apply Default ${HF.capitalizeEachWord( + theme + )} Theme`, + explanation: `This will overwrite any custom colors you've picked out`, + buttons, + }; + await HF.createDialog("Apply Default Theme", templatePath, data); + } + } + + async _updateObject(event, formData) { + const autoContrast = await HF.getSettingValue( + "artGallerySettings", + "colorSchemeData.autoContrast" + ); + if (autoContrast) { + //if auto-contrast is turned on + const bgColorKey = "colorSchemeData.colors.backgroundColor"; + for (const key in formData) { + if (key.includes(".colors.") && !key.includes(bgColorKey)) { + //for all the colors, convert the colors + const bgColor = formData[bgColorKey]; + const fgColor = formData[key]; + const hasEnoughContrast = HF.checkIfColorsContrastEnough( + bgColor, + fgColor + ); + //only alter the contrast if there isn't enough already + if (!hasEnoughContrast) { + formData[key] = HF.getColorWithContrast(bgColor, fgColor); + } + } + } + } + + await game.JTCS.utils.setSettingValue("artGallerySettings", formData, "", true); + await game.JTCSSettingsApp.render(true); + } +} + +window.JTCSSettingsApplication = JTCSSettingsApplication; diff --git a/scripts/classes/MultiMediaPopout.js b/scripts/classes/MultiMediaPopout.js new file mode 100644 index 0000000..9bbefc4 --- /dev/null +++ b/scripts/classes/MultiMediaPopout.js @@ -0,0 +1,69 @@ +/* Code below written by zeelo1 https://github.com/zeel01/TokenHUDArtButton/blob/master/artbutton.js -- to handle videos in the popout +/* with some tweaks to fit my module +/** + * Capable of handling images, as well as .mp4 and .webm video + * not very sophisticated. + * + * @class ImageVideoPopout + * @extends {ImagePopout} + */ + +export default class ImageVideoPopout extends ImagePopout { + /** + * Creates an instance of MultiMediaPopout. + * + * @param {string} src + * @param {object} [options={}] + * @memberof ImageVideoPopout + */ + constructor(src, options = {}) { + super(src, options); + + this.video = [".mp4", "webm"].includes( + src.slice(-4).toLowerCase() + ); + + this.options.template = "modules/journal-to-canvas-slideshow/templates/media-popout.html"; + } + + /** @override */ + async getData(options) { + let data = await super.getData(); + data.isVideo = this.video; + return data; + } + /** + * Share the displayed image with other connected Users + */ + shareImage() { + game.socket.emit("module.journal-to-canvas-slideshow", { + image: this.object, + title: this.options.title, + uuid: this.options.uuid + }); + } + + /** + * Handle a received request to display media. + * + * @override + * @param {string} image - The path to the image/media resource. + * @param {string} title - The title for the popout title bar. + * @param {string} uuid + * @return {ImageVideoPopout} + * @private + */ + static _handleShareMedia({ image, title, uuid } = {}) { + return new ImageVideoPopout(image, { + title: title, + uuid: uuid, + shareable: false, + editable: false + }).render(true); + } +} + + +Hooks.once("ready", () => { + game.socket.on("module.journal-to-canvas-slideshow", ImageVideoPopout._handleShareMedia); +}); \ No newline at end of file diff --git a/scripts/classes/PopoverGenerator.js b/scripts/classes/PopoverGenerator.js new file mode 100644 index 0000000..358d479 --- /dev/null +++ b/scripts/classes/PopoverGenerator.js @@ -0,0 +1,270 @@ +"use strict"; +import { HelperFunctions } from "./HelperFunctions.js"; +import { universalInterfaceActions as UIA } from "../data/Universal-Actions.js"; +export class Popover { + static defaultElementData = { + popoverElement: { + target: null, + hideEvents: [], + }, + sourceElement: { + target: null, + hideEvents: [], + }, + parentElement: { + target: null, + hideEvents: [], + }, + }; + static async processPopoverData( + sourceElement, + parentElement, + templateData, + elementData = Popover.defaultElementData, + sourceEvent = "" + ) { + // -- RENDER THE POPOVER + elementData.parentElement.target = parentElement; + elementData.sourceElement.target = sourceElement; + + let elementDataArray = Object.keys(elementData).map((key) => { + let newData = elementData[key]; + newData.name = key; + return newData; + }); + + let popover = await Popover.createAndPositionPopover( + templateData, + elementDataArray, + sourceEvent + ); + + return popover; + } + + /** + * + * @param {Event} event - the event (usually hover) that generated the tooltip + * @param {JQueryObject} $html - the html of the entire app + * @param {String} dataElementSelector - a string to select the parent element with the relevant data for this tooltip + */ + static async generateTooltip(event, $html, dataElementSelector, sourceEvent) { + if (!$html.jquery) $html = $($html); + + let sourceElement = event.currentTarget; + let parentDataElement = sourceElement.closest(dataElementSelector); + + let templateData = Popover.createTemplateData(parentDataElement, "tooltip", { + content: sourceElement.dataset.tooltipText, + }); + let popover = await Popover.processPopoverData( + sourceElement, + $html, + templateData, + { + ...Popover.defaultElementData, + }, + sourceEvent + ); + let eventString = "mouseenter mouseleave"; + let selectorString = `[data-tooltip], .popover[data-popover-id="tooltip"]`; + $html + .off(eventString, selectorString) + .on(eventString, selectorString, async (event) => { + let { type, currentTarget } = event; + let isMouseOver = type === "mouseover" || type === "mouseenter"; + !currentTarget.jquery && (currentTarget = $(currentTarget)); + + //if our current target is our source element or the popover itself + if (currentTarget.is($(sourceElement)) || currentTarget.is($(popover))) { + if (!popover.hoveredElements) popover.hoveredElements = []; + let { hoveredElements } = popover; + let isInArray = Popover.JqObjectInArray( + hoveredElements, + currentTarget + ); + if (isMouseOver) { + //if we're already tracking it, remove it + if (!isInArray) { + hoveredElements.push(currentTarget); + } else { + } + popover.hoveredElements = hoveredElements; + } else { + if (isInArray) { + //if we're already tracking it, remove it + hoveredElements = hoveredElements.filter( + (el) => !el.is(currentTarget) + ); + } + popover.hoveredElements = hoveredElements; + if (popover.hoveredElements.length === 0) { + await Popover.hideAndDeletePopover(popover); + } + } + } + }); + } + + static JqObjectInArray(objectArray, searchObject) { + let isIncluded = false; + objectArray.forEach((obj) => { + if (obj.is(searchObject)) { + isIncluded = true; + } + }); + return isIncluded; + } + + static createTemplateData(parentLI, partialName, context = {}) { + let template = { frameId: "", id: "", type: "" }; + let dataset = filterObject($(parentLI).data(), template); + if (!dataset) { + dataset = {}; + } + let popoverId = partialName; + + dataset.popoverId = popoverId; //to keep there from being multiples of the same popover + return { + passedPartial: partialName, + dataset: dataset, + passedPartialContext: context, + }; + } + + static validateInput(inputValue, validationType, onInvalid = "") { + let valid = false; + switch (validationType) { + case "image": + valid = HelperFunctions.isImage(inputValue); + break; + default: + valid = inputValue !== undefined; + break; + } + return valid; + } + + /** + * Generates a popover (tooltip, etc), and positions it from the source element + * boundingClientRect data + * @param {Object} templateData - the data to be passed to the popover + * @param {Application} parentApp - the parent application rendering the popover + * @param {HTMLElement} sourceElement - the element that is the "source" of the popover (a button, input, etc.) + */ + static async createAndPositionPopover( + templateData, + elementDataArray = [], + sourceEvent = "" + ) { + let elements = elementDataArray.map((data) => data.target); + let [popoverElement, sourceElement, parentElement] = elements; //destructure the passed-in elements + + let boundingRect = sourceElement.getBoundingClientRect(); + + let popoverTemplate = game.JTCS.templates["popover"]; + // popoverElement = parentElement.find(`.popover[data-popover-id="${templateData.dataset.popoverId}"]`); + let another = parentElement.find(`.popover`); + popoverElement = parentElement.find( + `.popover[data-popover-id="${templateData.dataset.popoverId}"]` + ); + let areTheSame = another.is(popoverElement); + if (another && !areTheSame) { + await Popover.hideAndDeletePopover(another); + } + + if (popoverElement.length === 0) { + //if it doesn't already exist, create it + let renderedHTML = await renderTemplate(popoverTemplate, templateData); + parentElement.append(renderedHTML); + popoverElement = parentElement.find( + `.popover[data-popover-id="${templateData.dataset.popoverId}"` + ); + UIA.fade(popoverElement); + } + + let popoverData = elementDataArray.find((data) => data.name === "popoverElement"); + + popoverData.target = popoverElement; + + popoverElement.css({ position: "absolute" }); + popoverElement.offset({ + top: boundingRect.top + boundingRect.height, + left: boundingRect.left, + }); + popoverElement.focus({ focusVisible: true }); + + //set up a "Click Out" event handler + let popoverId = templateData.dataset.popoverId; + // //hideEvents should be list of events to hide the popover on (like blur, change, mouseout, etc) + // elementDataArray.forEach((data) => { + // let targetElement = data.target; + + // data.hideEvents.forEach((eventData) => { + // let handler; + // let selector; + // let eventName; + // let options; + + // if (typeof eventData === "string") { + // //if it's a simple string, just set the handler to immediaetly hide the popover on this event + // eventName = eventData; + // handler = async (event) => await Popover.hideAndDeletePopover(popoverElement); + // selector = "*"; + // } else if (typeof eventData === "object") { + // //if it's an object, we'll want to do something (like validate input) first before hiding + // eventName = eventData.eventName; + + // //pass the popover element and the hide function to the wrapperFunction + // options = { + // ...eventData.options, + // popover: popoverElement, + // hideFunction: Popover.hideAndDeletePopover, + // }; + // handler = async (event, options) => { + // await eventData.wrapperFunction(event, options); + // }; + // selector = eventData.selector; + // } + // $(targetElement) + // .off(eventName, selector) + // .on(eventName, selector, async (event) => await handler(event, options)); + // }); + // }); + // let popoverID = popoverElement.data().popoverId; + $(document) + .off("click") + .on("click", async (event) => { + //make sure the button that originated the click wasn't + //the same one being handled by this document + if (Popover.isOutsideClick(event, sourceElement)) { + await Popover.hideAndDeletePopover(popoverElement); + } + }); + + return popoverElement; + } + + static isOutsideClick(event, sourceElement) { + let wasOnPopover = $(event.target).closest(".popover").length > 0; + let wasOnSourceElement = $(event.target).is($(sourceElement)); + if (wasOnPopover || wasOnSourceElement) { + //click was on the popover + return false; + } + //if our click is outside of our popover element + return true; + } + + static async hideAndDeletePopover(popoverElement) { + if (popoverElement.timeout) { + //if the popover is already counting down to a timeout, cancel it + clearTimeout(popoverElement.timeout); + } + let popoverTimeout = setTimeout(() => { + UIA.fade(popoverElement, { isFadeOut: true }); + }, 400); + //save that timeout's id on the popover + popoverElement.timeout = popoverTimeout; + } +} diff --git a/scripts/classes/PopoverGenerator.test.js b/scripts/classes/PopoverGenerator.test.js new file mode 100644 index 0000000..266c12d --- /dev/null +++ b/scripts/classes/PopoverGenerator.test.js @@ -0,0 +1 @@ +const dummyData = {}; diff --git a/scripts/classes/SheetImage.test.js b/scripts/classes/SheetImage.test.js new file mode 100644 index 0000000..4504f87 --- /dev/null +++ b/scripts/classes/SheetImage.test.js @@ -0,0 +1,166 @@ +import { HelperFunctions } from "./HelperFunctions.js"; +import { TestUtils } from "../tests/test-utils.js"; +import { ArtTileManager } from "./ArtTileManager.js"; + +export async function sheetImageDisplayTest(context) { + const { describe, it, assert, expect, should } = context; + + describe("Sheet image display suite", async function () { + const { + dispatchChange, + dispatchMouseDown, + getTileObject, + getDocData, + resizeTile, + changeTileImage, + getArtGallerySettings, + getDocIdFromApp, + getAppFromWindow, + getAreasOfDocs, + deleteTestScene, + duplicateTestScene, + initializeScene, + returnClassListAsArray, + returnComputedStyles, + checkAppElementForId, + getChildElements, + clickButton, + clickActionButton, + } = TestUtils; + + let sheetApp, + sheetElement, + scene, + sheetControls, + imageControls, + clickableImageContainers, + ourImageContainer, + artJournal, + artScene; + const clickableContainerSelector = ".clickableImageContainer"; + const imageControlsSelector = ".clickableImageControls"; + const sheetControlsSelector = "#sheet-controls"; + + let sheetGlobalActionStrings = ["togglelmageControls", "openSlideshowConfig", "openSettingsApp", "fadeJournal"]; + + /** + * set the sheet data from the test journal + */ + async function getSheetData() { + sheetControls = getChildElements(sheetElement, sheetControlsSelector); + + imageControls = getChildElements(sheetElement, imageControlsSelector, true); + + clickableImageContainers = getChildElements(sheetElement, clickableContainerSelector, true); + ourImageContainer = clickableImageContainers[1]; + } + + before(async () => { + let sourceScene = await initializeScene("Premade Gallery Scene"); + scene = await duplicateTestScene(sourceScene); + // sheetApp = await game.journal.getName("Art").sheet._render(true); + sheetApp = await game.journal.getName("Art").sheet.render(true); + await quench.utils.pause(900); + sheetElement = sheetApp.element; + await getSheetData(); + }); + + after(async () => { + await deleteTestScene(scene); + }); + + async function getDefaultDisplayIDs() { + let artSceneID = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.scene.value" + ); + let artJournalID = await HelperFunctions.getSettingValue( + "artGallerySettings", + "dedicatedDisplayData.journal.value" + ); + artScene = game.scenes.get(artSceneID); + artJournal = game.scenes.get(artJournalID); + } + describe("Testing individual image controls", async function () { + let ourImage; + let src; + let displayMethod = "window"; + const clickImageControlButton = async () => { + ourImageContainer.querySelector(`[data-method='${displayMethod}']`).click(); + await quench.utils.pause(900); + }; + async function compareWindowContent(type, displayMethodAppName, searchText = "") { + let windowApp = getAppFromWindow(type, searchText); + let windowElement = windowApp.element; + assert.exists(windowElement[0]); + let windowImageSrc = windowElement[0].querySelector("img")?.getAttribute("src"); + + if (game.version < 10 && displayMethodAppName.toLowerCase().includes("journal")) + windowImageSrc = returnComputedStyles(windowElement[0], ".lightbox-image", "background-image"); + // windowImageSrc = getComputedStyle( + // windowElement[0].querySelector(".lightbox-image") + // ).getPropertyValue("background-image"); + let message = `${displayMethodAppName} src should equal clicked image src`; + if (game.version >= 10) expect(windowImageSrc, message).to.equal(src); + else expect(windowImageSrc, message).to.have.string(src); + windowApp.close(); + } + async function compareTileContent(ourScene) { + let defaultArtTileID = await ArtTileManager.getDefaultArtTileID(ourScene); + let tileDoc = await getTileObject(defaultArtTileID, ourScene.id); + let textureProperty = game.version >= 10 ? "texture.src" : "img"; + let tileSrc = await getDocData(tileDoc, textureProperty); + expect(tileSrc, `${displayMethod} image src should equal clicked image src`).to.equal(src); + } + before(async () => { + await getDefaultDisplayIDs(); + ourImage = ourImageContainer.querySelector("img"); + src = ourImage.getAttribute("src"); + }); + let doBeforeEach = clickImageControlButton; + beforeEach(doBeforeEach); + afterEach(async () => { + await getSheetData(); + }); + it("Renders a popout window with the apporpriate image", async () => { + await compareWindowContent(ImagePopout, "ImagePopout"); + displayMethod = "journalEntry"; + }); + it("Renders the art journal sheet with the appropriate image", async () => { + await compareWindowContent(JournalSheet, "Art Journal", "Display Journal"); + // //TODO: Ensure the id of this journal entry matches the Art Journal entry + + displayMethod = "artScene"; + }); + it("Updates the default Art Tile in the Art Scene with the appropriate image", async () => { + await compareTileContent(artScene); + displayMethod = "anyScene"; + }); + it("Updates the default Art Tile in the current scene with the appropriate image", async () => { + let tiles = scene.tiles.contents; + // compare the slideshow tiles to the tiles that are in the display + let buttons = Array.from(ourImageContainer.querySelectorAll(".displayTiles button")); + + for (let button of buttons) { + button.click(); + let id = button.dataset.id; + await quench.utils.pause(200); + let tile = tiles.find((tile) => tile.id === id); + let textureProperty = game.version >= 10 ? "texture.src" : "img"; + let tileSrc = await getDocData(tile, textureProperty); + let imgSrc = ourImage.getAttribute("src"); + expect(tileSrc, "Tile path should equal image path").to.equal(imgSrc); + } + + displayMethod = "anyScene"; + }); + doBeforeEach = async () => { + ourImage.click(); + await quench.utils.pause(900); + }; + it("Updates the Art Tile associated with the particular sheet image", async () => { + await compareTileContent(game.scenes.viewed); + }); + }); + }); +} diff --git a/scripts/classes/SlideshowConfig.test.js b/scripts/classes/SlideshowConfig.test.js new file mode 100644 index 0000000..7157b98 --- /dev/null +++ b/scripts/classes/SlideshowConfig.test.js @@ -0,0 +1,651 @@ +import { SlideshowConfig } from "../SlideshowConfig.js"; +import { HelperFunctions } from "./HelperFunctions.js"; +import { ArtTileManager } from "./ArtTileManager.js"; +import { ImageDisplayManager } from "./ImageDisplayManager.js"; +import { JTCSSettingsApplication } from "./JTCSSettingsApplication.js"; +import { TestUtils } from "../tests/test-utils.js"; +import { sheetImageDisplayTest } from "./SheetImage.test.js"; + +const slideshowConfigTest = async (context) => { + const { describe, it, assert, expect, should } = context; + const { + dispatchChange, + dispatchMouseDown, + getTileObject, + getDocData, + resizeTile, + changeTileImage, + getArtGallerySettings, + getDocIdFromApp, + getAppFromWindow, + getAreasOfDocs, + deleteTestScene, + duplicateTestScene, + initializeScene, + returnClassListAsArray, + returnComputedStyles, + checkAppElementForId, + getDefaultImageSrc, + } = TestUtils; + describe("Slideshow Config Test Suite", async function () { + let configApp, configElement, scene, defaultImageSrc; + + async function clickGlobalButton(actionName) { + configElement[0] + .querySelector( + `[data-action='globalActions.click.actions.${actionName}']` + ) + .click(); + await quench.utils.pause(900); + } + + /** + * @description - renders the Scene Config + */ + async function renderConfig() { + configApp = new SlideshowConfig(); + await configApp._render(true); + configElement = configApp.element; + } + async function getTestData() { + defaultImageSrc = await getArtGallerySettings( + "defaultTileImages.paths.artTilePath" + ); + } + before(async () => { + let sourceScene = await initializeScene(); + scene = await duplicateTestScene(sourceScene); + await getTestData(); + await renderConfig(); + }); + after(async () => { + await deleteTestScene(scene); + }); + describe("Testing Linked Gallery Tile Item Actions in overflow menu", async function () { + let tileID, + tileData, + tileDoc, + tileItemPrefixString, + ourTileElement, + tileElements, + overflowMenu, + ourButton, + originalTileID; + + before(async () => { + await getTileData(); + originalTileID = tileID; //store the very first tile + }); + beforeEach(async () => { + await doBeforeEach(); + }); + let doBeforeEach = async () => { + await toggleOverflowMenu(); + await changeTileImage(tileID, defaultImageSrc); + await getTileData(); + }; + + async function getTileData() { + tileID = await ArtTileManager.getDefaultArtTileID(scene); + tileData = await ArtTileManager.getGalleryTileDataFromID(tileID); + tileDoc = await getTileObject(tileID); + tileItemPrefixString = "[data-action='itemActions.click.actions."; + tileElements = $(configElement).find( + `.tile-list-item:not([data-is-default])` + ); + ourTileElement = $(configElement).find( + `.tile-list-item[data-is-default]` + )[0]; + } + + // get tile Item id + async function clickActionButton(actionName, element = overflowMenu) { + const actionQueryString = combine(actionName); + + await clickButton(element, actionQueryString); + } + async function clickButton(element, selector) { + ourButton = element.querySelector(selector); + ourButton.click(); + await quench.utils.pause(900); + } + + // async function getFameTileData() { + // let frameTileID = tileData.linkedBoundingTile; + // let frameTileData = await ArtTileManager.getGalleryTileDataFromID( + // frameTileID + // ); + // return frameTileData; + // } + + function combine(actionName) { + let string = `${tileItemPrefixString}${actionName}']`; + return string; + } + async function toggleOverflowMenu() { + await clickActionButton("toggleOverflowMenu", ourTileElement); + //get the popover, which will be a child of the parent app's element + overflowMenu = configElement[0].querySelector(".popover"); + return overflowMenu; + } + it("renders the overflow menu when the 'toggle overflow menu'", async function () { + assert.notEqual( + ourTileElement, + undefined, + "Our tile element should be defined" + ); + + assert.notEqual( + overflowMenu, + undefined, + "Our overflow menu element should be defined" + ); + }); + + it("selects the tile when the 'Select Tile' action is clicked", async function () { + await clickActionButton("selectTile"); + + const layerName = game.version >= 10 ? "tiles" : "background"; + const selectedTileID = canvas[layerName].controlled[0].id; + expect(selectedTileID).to.equal(tileID); + }); + + it("renders the Tile's configuration app when the 'render tile config' action is clicked", async () => { + await clickActionButton("renderTileConfig"); + const app = getAppFromWindow(TileConfig); + let id = getDocIdFromApp(app); + expect(id).to.equal(tileID); + await app.close(); + }); + + it("Updates the tile's dimensions to be within that of its frame tile's dimensions when the 'Fit Tile to Frame' button is clicked", async () => { + //resize the tile to be bigger than scene + await resizeTile(tileDoc, scene); + + //get the frame data, whether frame tile or scene + let { linkedBoundingTile } = tileData; + let frameDoc; + if (linkedBoundingTile) { + frameDoc = await getTileObject(linkedBoundingTile); + } else { + frameDoc = game.scenes.viewed; + } + //get the area of the frame, and that of the art tile while resized (which should be bigger) + const { frameArea, artArea: oldArtArea } = await getAreasOfDocs( + frameDoc, + tileDoc + ); + expect(oldArtArea).to.be.above(frameArea); + // press the Fit Tile to Frame Button + await clickActionButton("fitTileToFrame"); + // get the new area of the art tile + const { artArea: newArtArea } = await getAreasOfDocs(frameDoc, tileDoc); + + //ensure the art tile area is smaller than the frame's area now + expect(newArtArea).to.be.below(frameArea); + }); + it("resets the Tile Image back to default when Clear Tile Image button is clicked", async () => { + if (!defaultImageSrc) { + defaultImageSrc = await getDefaultImageSrc("art"); + } + assert.isDefined(defaultImageSrc, "default image source is defined"); + //change the current tile's image (without url, will default to another image) + await changeTileImage(tileID); + + // get tile's current image + let textureProperty = game.version >= 10 ? "texture.src" : "img"; + const oldImgSrc = await getDocData(tileDoc, textureProperty); + + assert.isDefined(oldImgSrc, "Image source is defined"); + //check to see if it changed + expect( + oldImgSrc, + "Tile's old texture src should NOT equal default image path" + ).to.not.equal(defaultImageSrc); + + // * & click Clear Tile Image Button + await clickActionButton("clearTileImage"); + const newImgSrc = await getDocData(tileDoc, textureProperty); + // - test Tile Image now matches 'default' image saved + expect( + newImgSrc, + "Tile's texture src should equal default image path" + ).to.equal(defaultImageSrc); + }); + + /** + * simulates clicking the "deleteTileData" action button on a tile item + * @returns returns the dialog rendered after the click + */ + async function clickDeleteTileDataButton() { + //click "Delete Gallery Tile Data" Button and wait for prompt + + await clickActionButton("deleteTileData", overflowMenu); + // - ~ Prompt is created, asking if user wants to delete data + let app = getAppFromWindow(Dialog); + let dialogElement = app.element; + //get the dialog element from ui.windows + return { app, dialogElement }; + } + + it("renders a Delete Data confirmation prompt", async () => { + //click "Delete Gallery Tile Data" Button + let { dialogElement, app } = await clickDeleteTileDataButton(); + assert.isDefined(dialogElement, "Delete dialog is defined"); + expect(app.element.text()).to.include("Delete"); + await app.close(); + }); + it("deletes the tile data on 'Delete'", async () => { + // ! - & On approve, dialog closes, tile data is deleted + let { dialogElement } = await clickDeleteTileDataButton(); + await clickButton(dialogElement[0], ".dialog-button.delete"); + // - ~ Scene Config App updates + let sceneTiles = await ArtTileManager.getSceneSlideshowTiles("art", true); + let ourOriginalTile = sceneTiles.find((tile) => tile.id === tileID); + expect(ourOriginalTile).to.not.exist; + await getTileData(); + }); + + it("closes the dialog button on cancel", async () => { + // ? - & On cancel, dialog closes, nothing happens + let { dialogElement } = await clickDeleteTileDataButton(); + await clickButton(dialogElement[0], ".dialog-button.cancel"); + //app should be undefined now after cancel is clicked + let app = getAppFromWindow(Dialog, "Delete URL"); + assert.isUndefined(app); + }); + async function renderURLSharePopover() { + await clickActionButton("shareURLOnTile", ourTileElement); + let popoverId = "input-with-error"; + let urlShareElement = configElement[0].querySelector( + `.popover[data-popover-id='${popoverId}']` + ); + let inputBox = urlShareElement.querySelector("input"); + return { urlShareElement, inputBox }; + } + it("renders the SHARE URL IMAGE popover box", async () => { + doBeforeEach = async () => { + await getTileData(); + }; + // Click on "Share URL Image" button + let { urlShareElement, inputBox } = await renderURLSharePopover(); + assert.exists(urlShareElement, "the popover should exist"); + assert.exists(inputBox, "the input box should exist"); + }); + it("on change, notifies the user when an invalid url is provided", async () => { + let { inputBox } = await renderURLSharePopover(); + inputBox.value = "test"; + dispatchChange(inputBox); + quench.utils.pause(100); + let notification = ui.notifications.active[0][0]; + assert.exists(notification, "The notification exists"); + let includesErrorClass = notification.classList.contains("error"); + + assert.isTrue(includesErrorClass, "This is an error notification"); + }); + it("on change, if url is valid and not CORS, sets the tile image to equal the url", async () => { + let { inputBox } = await renderURLSharePopover(); + let textureProperty = game.version >= 10 ? "texture.src" : "img"; + let oldImg = await getDocData(tileDoc, textureProperty); + //enter placeholder png + inputBox.value = + "https://images.pexels.com/photos/934067/pexels-photo-934067.jpeg"; + dispatchChange(inputBox); + await quench.utils.pause(900); + await getTileData(); //reseting the tile data again + let newImg = await getDocData(tileDoc, textureProperty); + expect(newImg, "New Tile Image shouldn't Equal old img").to.not.equal( + oldImg + ); + }); + it("sets the tile to be the default tile", async () => { + // Ctrl + Click (Set Default Action) + let otherTileElement = Array.from(tileElements).filter( + (tileEl) => tileEl.dataset.type === "art" + )[0]; + assert.exists(otherTileElement, "A secondary tile element exists"); + let oldDefaultID = tileID; + + dispatchMouseDown(otherTileElement); //dispatching an event witfh ctrl pressed? + await quench.utils.pause(900); + // - ~ Tile is set as default tile in current scene + let newDefaultId = await ArtTileManager.getDefaultArtTileID(scene); + expect(newDefaultId).to.not.equal(oldDefaultID); + //TODO - test border colors and indicator colors + // - $ Tile Item now has color change to match. + // - $ Tile Indicator Color changes to match default color as set in settings + }); + }); + describe("Slideshow config global actions", async () => { + before(async () => { + await renderConfig(); + }); + it("renders the JTCS Art Gallery settings Application", async () => { + await clickGlobalButton("showModuleSettings"); + let app = getAppFromWindow(JTCSSettingsApplication); + assert.exists(app, "The app has been rendered"); + await app.close(); + }); + it("renders the URL Share Dialog", async () => { + await clickGlobalButton("showURLShareDialog"); + let app = getAppFromWindow(Dialog); + assert.exists(app, "The app has been rendered"); + expect(app.element.text()).to.include("Share URL"); + await app.close(); + }); + it("fades out the Scene Gallery Config", async () => { + let classList; + let opacityValue; + const recheckClassList = () => { + let el = configElement; + let sel = ".window-content"; + classList = returnClassListAsArray(el, sel); + opacityValue = parseFloat(returnComputedStyles(el, sel, "opacity")); + }; + //by default shouldn't include fade + recheckClassList(); + expect(classList).to.not.include("fade"); + expect(opacityValue).to.equal(1.0); + + //should include fade after fade button is clicked + await clickGlobalButton("toggleSheetOpacity"); + recheckClassList(); + expect(classList).to.include("fade"); + //check opacity + expect(opacityValue).to.equal(0.5); + + //should not includ fade after fade button is clicked once more + await clickGlobalButton("toggleSheetOpacity"); + recheckClassList(); + expect(classList).to.not.include("fade"); + expect(opacityValue).to.equal(1.0); + }); + }); + }); +}; +const unlinkedTilesTest = async (context) => { + const { describe, it, assert, expect, should } = context; + const { + testFitToFrame, + getDefaultImageSrc, + renderConfig, + dispatchEvent, + getTileObject, + getDocData, + changeTileImage, + getArtGallerySettings, + deleteTestScene, + duplicateTestScene, + initializeScene, + returnClassListAsArray, + returnComputedStyles, + } = TestUtils; + const itemActionPrefixString = "[data-action='itemActions.click.actions."; + let configApp, configElement, scene, defaultImageSrc, newArtTileBtn, newFrameTileBtn; + async function bundleTestData() { + let sourceScene = await initializeScene("Empty Tile Scene"); + scene = await duplicateTestScene(sourceScene); + await quench.utils.pause(300); + + defaultImageSrc = await getDefaultImageSrc(); + ({ configApp, configElement } = await renderConfig()); + + newArtTileBtn = configElement.find( + ".wrapper.art-tiles .new-tile-list-item button" + ); + newFrameTileBtn = configElement.find( + ".wrapper.frame-tiles .new-tile-list-item button" + ); + } + //reset all of the references + async function getConfigData() { + configElement = $(document.documentElement.querySelector("#slideshow-config")); + newArtTileBtn = configElement.find( + ".wrapper.art-tiles .new-tile-list-item button" + ); + newFrameTileBtn = configElement.find( + ".wrapper.frame-tiles .new-tile-list-item button" + ); + } + describe("It tests the creation and linking of new tiles", async function () { + before(async () => { + await bundleTestData(); + }); + after(async () => { + await deleteTestScene(scene); + }); + // beforeEach(async ()=> { + // await getConfigData() + // }) + async function getTileListItem(type) { + let tileListElement = configElement.find( + `.tile-list-item[data-type='${type}']:not(.new-tile-list-item)` + )[0]; + return tileListElement; + } + async function createNewTileListItem(type) { + let btn = type === "art" ? newArtTileBtn : newFrameTileBtn; + btn.click(); + await quench.utils.pause(900); + await getConfigData(); + return await getTileListItem(type); + let tileListElement = configElement.find( + `.tile-list-item[data-type='${type}']:not(.new-tile-list-item)` + )[0]; + return tileListElement; + } + function getInlineBadge(tileListElement) { + let unlinkedNotificationBadge = tileListElement.querySelector( + ".inline-notification[data-variant='warning']" + ); + return unlinkedNotificationBadge; + } + async function checkInlineBadge(tileListElement, shouldExist = true) { + let unlinkedNotificationBadge = getInlineBadge(tileListElement); + if (shouldExist) { + assert.exists( + unlinkedNotificationBadge, + "The unlinked notification badge should exist" + ); + } else { + expect( + unlinkedNotificationBadge, + "The unlinked notification badge should not exist" + ).to.not.exist; + } + } + async function clickUnlinkedActionButton(actionName, type) { + const fullActionString = `${itemActionPrefixString}${actionName}']`; + const tileListItem = await getTileListItem(type); + const btn = tileListItem.querySelector(fullActionString); + assert.exists(btn); + btn.click(); + await quench.utils.pause(900); + await getConfigData(); + // return await getConfigData(); + } + + async function createAndCheckNewLinkedTile(type) { + let tiles; + + /** + * Get the tiles in the scene + */ + function getTiles() { + tiles = scene.tiles.contents; + } + + getTiles(); + let length = tiles.length; + // expect(tiles, "Scene should have no tiles at first").to.be.empty; + const actionName = "createNewGalleryTile"; + await clickUnlinkedActionButton(actionName, type); + + getTiles(); + expect(tiles).to.have.length.above(length); + + //STUB - get the tile list item element + let tileListElement = await getTileListItem(type); + let tileID = tileListElement.dataset.id; + let tileDoc = await getTileObject(tileID); + let textureProperty = game.version >= 10 ? "texture.src" : "img"; + let tileDocSrc = await getDocData(tileDoc, textureProperty); + let tileDocID = tileDoc.id; //await getDocData(tileDoc, "id"); + + //STUB - Test that the Tile ID doesn't contain "unlinked" anymore + expect( + tileID, + "Tile id shouldn't have 'unlinked' anymore" + ).to.not.contain.oneOf(["unlinked"]); + + //STUB - Test that the Tile has the Default Frame Tile Image as stored in the settings + let defaultSrc = await getDefaultImageSrc(type); + expect( + tileDocSrc, + "The tile should have the same image as the defalt frame tile image" + ).to.equal(defaultSrc); + + // STUB - test that the Tile List Item no longer has the 'unlinked' badge + await checkInlineBadge(tileListElement, false); + + //STUB - Test that the created tile's id and the current tile's id now match + expect(tileID).to.equal(tileDocID); + } + + function getSelectElementAndOptions(tileListItem) { + let frameSelect = tileListItem.querySelector("select"); + let options = Array.from(frameSelect.querySelectorAll("option")); + return { + frameSelect, + options, + }; + } + + async function clickOptionElement(tileListItem) { + let { frameSelect, options } = getSelectElementAndOptions(tileListItem); + let frameOption1 = options[1]; + assert.exists(frameOption1); + + let optionValue = frameOption1.value; + frameSelect.value = optionValue; + await quench.utils.pause(400); + } + it("creates a new Scene Gallery Tile Element in the config, when 'new art tile button' is clicked", async function () { + let tileListElement = await createNewTileListItem("art"); + assert.exists( + tileListElement, + "This new art Gallery Tile List Item element should exist" + ); + await checkInlineBadge(tileListElement); + + // TODO - assert also that the art tile has the Canvas as its frame + }); + it("Creates a new Frame Gallery Tile Element in the config, when the 'new Frame Tile Button' is clicked", async function () { + let tileListElement = await createNewTileListItem("frame"); + assert.exists( + tileListElement, + "This new FRAME Gallery Tile List Item element should exist" + ); + await checkInlineBadge(tileListElement); + }); + + it("Creates a new ART tile on the canvas when the 'Button' is pressed", async function () { + await createAndCheckNewLinkedTile("art"); + }); + it("Creates a new FRAME tile on the canvas when the 'Button' is pressed", async function () { + await createAndCheckNewLinkedTile("frame"); + }); + it("Checks to see if selecting an Art Tile for the Frame Tile updates the art tile in the config", async function () { + // test that the selected Frame Tile updates in the config + let frameSelect, options, selectedValue, tileListItem; + async function getFrameAndTileListItem() { + tileListItem = await getTileListItem("art"); + } + async function getFrameSelectAndOptions() { + tileListItem = await getTileListItem("art"); + frameSelect = getSelectElementAndOptions(tileListItem).frameSelect; + options = getSelectElementAndOptions(tileListItem).options; + assert.exists(frameSelect); + selectedValue = frameSelect.value; + } + // get the selected option element, and click it, then wait + await getFrameSelectAndOptions(); + let oldValue = selectedValue; + + await clickOptionElement(tileListItem); + + //get the selected option and elements again, and check the select's value + await getFrameSelectAndOptions(); + + //expect the option element to be a different element now + expect( + selectedValue, + "Selected frame Value should have changed" + ).to.not.equal(oldValue); + + // await getConfigData(); + + const frameTileID = selectedValue; + const artTileID = tileListItem.dataset.id; + + const frameTileListItem = configElement[0].querySelector( + `.tile-list-item[data-id='${frameTileID}']` + ); + + //TODO - uncomment the below code, and test the border color upon hover + // dispatchEvent(tileListItem, MouseEvent, "mouseover"); + // await quench.utils.pause(100); + + // let artBorderColor = returnComputedStyles(tileListItem, "", "border-color"); + // let frameBorderColor = returnComputedStyles( + // frameTileListItem, + // "", + // "border-color" + // ); + + // test that the Art Tile fits within the designated Frame Tile + const url = + "https://images.pexels.com/photos/934067/pexels-photo-934067.jpeg"; + + //update the art tile, and see if it fits within the frame tile now + await ImageDisplayManager.updateTileObjectTexture( + artTileID, + frameTileID, + url, + "anyScene" + ); + + let { artArea, frameArea } = await testFitToFrame(frameTileID, artTileID); + expect( + artArea, + "The art area should be smaller than the frame area" + ).to.be.below(frameArea); + }); + }); +}; + +const settingsToggleTest = () => {}; + +Hooks.on("quenchReady", async (quench) => { + quench.registerBatch( + "Slideshow Config Test", + async (context) => { + await slideshowConfigTest(context); + }, + { displayName: "QUENCH: SlideshowConfig Test Suite" } + ); + quench.registerBatch( + "Sheet image display test", + async (context) => { + await sheetImageDisplayTest(context); + }, + { displayName: "QUENCH: Image Display Test Suite" } + ); + quench.registerBatch( + "Config Unlinked Test", + async (context) => { + await unlinkedTilesTest(context); + }, + { displayName: "QUENCH: Unlinked tiles test" } + ); +}); diff --git a/scripts/data/JTCS-Actions.js b/scripts/data/JTCS-Actions.js new file mode 100644 index 0000000..b1b07fb --- /dev/null +++ b/scripts/data/JTCS-Actions.js @@ -0,0 +1,37 @@ +import { MODULE_ID } from "../debug-mode.js"; +import { ImageDisplayManager } from "../classes/ImageDisplayManager.js"; +export class JTCSActions { + static async onDisplayActionClick(event, options = {}) { + let { method, url } = options; + await ImageDisplayManager.determineDisplayMethod({ + method: method, + url: url, + }); + } + static displayActions = { + anyScene: { + label: "Current Scene", + icon: "fas fa-vector-square", + tooltip: "display image on the Default Art Tile in the current scene", + onClick: JTCSActions.onDisplayActionClick, + }, + window: { + label: "Popout Window", + icon: "fas fa-external-link-alt", + tooltip: "display image in pop-out window", + onClick: (event, options) => JTCSActions.onDisplayActionClick(event, "window", url), + }, + journalEntry: { + label: "Art Journal", + icon: "fas fa-book-open", + tooltip: "display image in your chosen 'Art Journal'", + onClick: JTCSActions.onDisplayActionClick, + }, + artScene: { + label: "Art Scene", + icon: "far fa-image", + tooltip: "display image in your chosen 'Art Scene'", + onClick: JTCSActions.onDisplayActionClick, + }, + }; +} diff --git a/scripts/data/ModuleManager.js b/scripts/data/ModuleManager.js new file mode 100644 index 0000000..1a15347 --- /dev/null +++ b/scripts/data/ModuleManager.js @@ -0,0 +1,28 @@ +import { ArtTileManager } from "../classes/ArtTileManager.js"; +import { CanvasIndicators } from "../classes/CanvasIndicators.js"; +import { HelperFunctions } from "../classes/HelperFunctions.js"; +import { ImageDisplayManager } from "../classes/ImageDisplayManager.js"; +import ImageVideoPopout from "../classes/MultiMediaPopout.js"; +import { SheetImageApp } from "../SheetImageApp.js"; +import { SheetImageDataController } from "../SheetImageDataController.js"; +import { SlideshowConfig } from "../SlideshowConfig.js"; + +const JTCSModules = { + ArtTileManager, + CanvasIndicators, + HelperFunctions, + ImageDisplayManager, + ImageVideoPopout, + // SheetImageApp, + SheetImageDataController, + SlideshowConfig, +}; +export const { + artTileManager, + canvasIndicatorManager, + helpers, + imageDisplayManager, + sheetImageApp, + sheetImageDataController, + slideshowConfig, +} = JTCSModules; diff --git a/scripts/data/SlideshowConfigActions.js b/scripts/data/SlideshowConfigActions.js new file mode 100644 index 0000000..0b11869 --- /dev/null +++ b/scripts/data/SlideshowConfigActions.js @@ -0,0 +1,880 @@ +"use strict"; +import { JTCSSettingsApplication } from "../classes/JTCSSettingsApplication.js"; +import { JTCSActions } from "./JTCS-Actions.js"; +import { Popover } from "../classes/PopoverGenerator.js"; +import { universalInterfaceActions as UIA } from "./Universal-Actions.js"; +import { HelperFunctions } from "../classes/HelperFunctions.js"; +import { ImageDisplayManager } from "../classes/ImageDisplayManager.js"; +import { ArtTileManager } from "../classes/ArtTileManager.js"; + +/** + * Show instructions + */ + +export const extraActions = { + renderTileConfig: async (event, options = {}) => { + let { tileID } = options; + await ArtTileManager.renderTileConfig(tileID); + }, + selectTile: async (event, options = {}) => { + let { tileID } = options; + await ArtTileManager.selectTile(tileID); + }, + updateTileData: async (event, options = {}) => { + let clickedElement = $(event.currentTarget); + + let { name, value } = event.currentTarget; + + let { tileID, app, parentLI } = options; + + let action = clickedElement.data().action || clickedElement.data().changeAction; + + let { type } = $(parentLI).data(); + + let isNewTile = false; + let isBoundingTile = type === "frame"; + + if (action.includes("createNewTileData")) { + isNewTile = true; + tileID = `unlinked${foundry.utils.randomID()}`; + name = "displayName"; + value = `Untitled ${type} Tile`; + } else { + // if we're already a Slideshow tile, look for our ID + tileID = clickedElement[0].closest(".tile-list-item, .popover").dataset.id; + } + + if (tileID) { + let updateData = { + id: tileID, + [name]: value, + ...(isNewTile ? { isBoundingTile: isBoundingTile } : {}), + }; + await ArtTileManager.updateSceneTileFlags(updateData, tileID); + await app.renderWithData(); + } + }, + deleteTileData: async (event, options = {}) => { + const { app, tileID, parentLI } = options; + let type = parentLI.dataset.type; + let displayName = await ArtTileManager.getGalleryTileDataFromID( + tileID, + "displayName" + ); + const templatePath = game.JTCS.templates["delete-confirmation-prompt"]; + const buttons = { + cancel: { + label: "Cancel", + icon: "", + }, + delete: { + label: "Delete Gallery Tile", + icon: "", + callback: async () => { + await ArtTileManager.deleteSceneTileData(tileID); + await app.renderWithData(); + }, + }, + }; + type = type.charAt(0).toUpperCase() + type.slice(1); + const data = { + icon: "fas fa-trash", + heading: "Delete Art Tile Data?", + destructiveActionText: `delete this ${type} tile data?`, + explanation: `This will permanently delete`, + lossDataList: [`${type} Tile '${displayName}'`], + explanation2: `With no way to get it back`, + buttons, + }; + await HelperFunctions.createDialog("Delete Art Tile", templatePath, data); + }, + highlightItemAndTile: async (event, options = {}) => { + let { parentLI, tileID, missing } = options; + // if (missing) { + // return; + // } + let { type, frameId: frameID } = $(parentLI).data(); + let isLeave = event.type === "mouseleave" ? true : false; + // we want every hover over a tile to highlight the tiles it is linked to + + let hoveredElement = $(event.currentTarget); + + // let type = hoveredElement.data().type; + // let id = hoveredElement.data().id; + + let otherListItems = []; + if (type === "frame") frameID = tileID; + + if (frameID) { + otherListItems = Array.from( + hoveredElement[0].closest(".tilesInScene").querySelectorAll("li") + ).filter( + //get list items with the opposite tile type + (item) => { + let passed = true; + if (item.dataset.type === type) { + passed = false; + } + if (item.dataset.flag === "ignoreHover") { + passed = false; + } + return passed; + } + ); + + //filter out list items + otherListItems = otherListItems.filter((element) => { + let dataset = Object.values({ ...element.dataset }).join(" "); + let match = false; + if (type === "art") { + //for art tiles, we're looking for frameTiles in the list that match the frame id + match = dataset.includes(frameID); + } else if (type === "frame") { + //for frame tiles, we're looking for art tiles in the list that have our id + match = dataset.includes(tileID); + } + return match; + }); + } + // if (!tileID) { + // return; + // } + let tile; + if (!missing) tile = await ArtTileManager.getTileObjectByID(tileID); + if (isLeave) { + hoveredElement.removeClass("accent border-accent"); + $(otherListItems).removeClass("accent border-accent"); + if (!missing) game.JTCS.indicatorUtils.hideTileIndicator(tile); + } else { + hoveredElement.addClass("accent border-accent"); + $(otherListItems).addClass("accent border-accent"); + if (!missing) game.JTCS.indicatorUtils.showTileIndicator(tile); + } + }, + setDefaultTileInScene: async (event, options = {}) => { + if (event.ctrlKey || event.metaKey) { + //if the ctrl or meta (cmd) key on mac is pressed + let { tileID, parentLI } = options; + let type = parentLI.dataset.type; + let tileInScene = await ArtTileManager.getTileObjectByID(tileID); + let displayName = await ArtTileManager.getGalleryTileDataFromID( + tileID, + "displayName" + ); + + if (tileInScene) { + await ArtTileManager.setDefaultArtTileID(tileID, game.scenes.viewed); + } else { + ui.notifications.warn( + `Gallery ${HelperFunctions.capitalizeEachWord(type)} + Tile "${displayName}" + must be linked to a tile in this scene + before it can be set as default` + ); + } + } + }, + showURLShareDialog: async (event, options = {}) => { + let wrappedActions = {}; + let { displayActions } = JTCSActions; + for (let actionName in displayActions) { + wrappedActions[actionName] = { ...displayActions[actionName] }; + + //converting properties to fit the dialog's schema + wrappedActions[ + actionName + ].icon = ``; + // wrappedActions[actionName].label = HelperFunctions.capitalizeEachWord(actionName, ""); + wrappedActions[actionName].callback = async (html) => { + let urlInput = html.find("input[name='urlInput']"); + let url = urlInput.val(); + if (url !== "") { + await ImageDisplayManager.determineDisplayMethod({ + method: actionName, + url: url, + }); + } + }; + } + delete wrappedActions.anyScene; + let buttons = { + ...wrappedActions, + cancel: { + label: "Cancel", + }, + }; + let templatePath = game.JTCS.templates["share-url-partial"]; + + await HelperFunctions.createDialog("Share URL", templatePath, { + buttons: buttons, + partials: game.JTCS.templates, + value: "", + }); + }, + showInstructions: async (event, options = {}) => { + const { html, type, isDefault, missing, frameId, tileID } = options; + const areVisible = await HelperFunctions.getSettingValue( + "areConfigInstructionsVisible" + ); + const instructionsElement = html.find("#JTCS-config-instructions"); + const isLeave = + event.type === "mouseleave" || event.type === "mouseout" ? true : false; + + if ((!instructionsElement && !isLeave) || !areVisible) { + //if the instructions element already exists and we're mousing over, or the instruction visibility has been toggled off + //return + return; + } + + let instructionsContent = "
"; + // add different instruction content depending on the tile's type (art or frame), whether it's unlinked/missing, and whether, if it's an art tile, it's currently set to the default art tile. + const defaultVariantText = isDefault ? "another" : "this"; + switch (type) { + case "art": + instructionsContent += `

+ ${isDefault ? "This art tile is set as Default." : ""} + Ctr + Click on ${defaultVariantText} Art Tile to set it to Default. +
+ If you don't specify where a Sheet Image will display, it will automatically display on the "Default" + Art Tile in a scene. +

`; + break; + case "frame": + if (!missing) { + instructionsContent += `

Frame tiles act as "boundaries" for Art Tiles, like a 'picture frame'.
+ When you link an Art Tile to a Frame tile, the Art Tile will get no larger than the Frame Tile.

`; + } + break; + } + if (missing) { + const tileName = await ArtTileManager.getGalleryTileDataFromID( + tileID, + "displayName" + ); + let suffix = `${HelperFunctions.capitalizeEachWord( + type + )} Tile "${tileName}"`; + instructionsContent += `

This ${HelperFunctions.capitalizeEachWord( + type + )} Tile tile is unlinked to any tile on the canvas in this scene.

+
    +
  • Click on to add a new Tile object to the canvas, linked to ${suffix}
  • +
  • Click on to link a pre-existing Tile object on the canvas to ${suffix}
  • +
+ + `; + } + if (type === "art" && !frameId) + instructionsContent += `

This Art Tile will be bound by the scene's canvas.

`; + else if (type === "art" && frameId) { + const frameTileName = await ArtTileManager.getGalleryTileDataFromID( + frameId, + "displayName" + ); + instructionsContent += `

This Art Tile will be bound by frame tile ${frameTileName}

`; + } + instructionsContent += "
"; + let content = instructionsElement.find(".instructions__content"); + instructionsElement.contentHidden = true; + + if (!isLeave) { + content.replaceWith(`${instructionsContent}`); + let element = instructionsElement[0]; + UIA.toggleShowAnotherElement(event, { + parentItem: element, + targetClassSelector: "instructions__content", + fadeIn: false, + }); + } else { + if (!instructionsElement.contentHidden) { + instructionsElement.contentHidden = true; + } + if (instructionsElement.timeout) { + clearTimeout(instructionsElement.timeout); + } + instructionsElement.timeout = setTimeout(async () => { + await UIA.fade(content, { + duration: 200, + isFadeOut: true, + onFadeOut: async () => { + content.replaceWith( + `
` + ); + instructionsElement.contentHidden = true; + // content.addClass("JTCS-hidden"); + }, + }); + }, 300); + } + }, + toggleInstructionsVisible: async (event, options = {}) => { + let areVisible = await HelperFunctions.getSettingValue( + "areConfigInstructionsVisible" + ); + await HelperFunctions.setSettingValue( + "areConfigInstructionsVisible", + !areVisible + ); + UIA.toggleActiveStyles(event); + }, + /** + * Lower the opacity of every other tile in the scene, to see this one more clearly + * This is disabled in v10, as the indicators are visible regardless + * @param {HTMLEvent} event - the triggering event + * @param {Object options - an options object + * @param {String} options.tileID - the ID of the Art Gallery tile we're operating upon + */ + toggleTilesOpacity: async (event, options = {}) => { + const { tileID } = options; + const btn = event.currentTarget; + const clickAction = btn.dataset.action; + const removeFade = btn.classList.contains("active") ? true : false; + const alphaValue = removeFade ? 1.0 : 0.5; + + //get other buttons from other Tile Items that may be set to active, and so we can toggle them off + let otherToggleFadeButtons = btn + .closest(".tilesInScene") + .querySelectorAll(`[data-action='${clickAction}'].active`); + + //filter any button that has the same parent tile item out + otherToggleFadeButtons = Array.from(otherToggleFadeButtons).filter( + (fadeBtn) => fadeBtn.closest(".tile-list-item").dataset.id !== tileID + ); + + const otherTiles = game.scenes.viewed.tiles.contents.filter( + (tile) => tile.id !== tileID + ); + + //check for game version + if (game.version >= 10) { + let update = otherTiles.map((data) => { + return { + _id: data.id, + alpha: alphaValue, + }; + }); + update.push({ _id: tileID, alpha: 1.0 }); + await game.scenes.viewed.updateEmbeddedDocuments("Tile", update); + } else { + otherTiles.forEach((tile) => { + tile.object.alpha = alphaValue; + }); + const ourTile = game.scenes.viewed.tiles.get(tileID); + ourTile.object.alpha = 1.0; + } + + //toggle this button active + if (otherToggleFadeButtons.length > 0) { + UIA.clearOtherActiveStyles( + event, + btn, + `[data-action='${clickAction}']`, + ".tilesInScene" + ); + } + UIA.toggleActiveStyles(event); + //toggle any other active buttons to be inactive + // Array.from(otherToggleFadeButtons).forEach((el) => UIA.toggleActiveStyles(event, el)); + }, + toggleSheetOpacity: async (event, options = {}) => { + UIA.fadeSheetOpacity(event); + UIA.toggleActiveStyles(event); + }, +}; +export const slideshowDefaultSettingsData = { + globalActions: { + click: { + propertyString: "globalActions.click.actions", + actions: { + showURLShareDialog: { + icon: "fas fa-external-link-alt", + tooltipText: "Share a URL link with your players", + onClick: extraActions.showURLShareDialog, + }, + showModuleSettings: { + icon: "fas fa-cog", + tooltipText: "Open JTCS Art Gallery Settings", + onClick: (event, options = {}) => { + UIA.renderAnotherApp("JTCSSettingsApp", JTCSSettingsApplication); + }, + }, + toggleInstructionsVisible: { + icon: "fas fa-eye-slash", + tooltipText: "toggle instruction visibility", + renderedInTemplate: true, + onClick: async (event, options) => + await extraActions.toggleInstructionsVisible(event, options), + }, + toggleSheetOpacity: { + icon: "fas fa-eye-slash", + tooltipText: "fade this application to better see the canvas", + onClick: async (event, options) => + extraActions.toggleSheetOpacity(event, options), + }, + // showArtScenes: { + // icon: "fas fa-map", + // tooltipText: "Show art scenes", + // onClick: async (event, options = {}) => { + // //display the art scenes (scenes that currently have slideshow data) + // let artScenes = await ArtTileManager.getAllScenesWithSlideshowData(); + // let chosenArtScene = await HelperFunctions.getSettingValue( + // "artGallerySettings", + // "dedicatedDisplayData.scene.value" + // ); + // let artSceneItems = {}; + + // artScenes.forEach((scene) => { + // artSceneItems[scene.name] = { + // icon: scene.thumbnail, + // dataset: {}, + // }; + // }); + + // let context = { + // propertyString: "globalActions.click.actions", + // items: artSceneItems, + // }; + // let templateData = Popover.createTemplateData(parentLI, "item-menu", context); + // let elementData = { ...Popover.defaultElementData }; + + // elementData["popoverElement"] = { + // targetElement: null, + // hideEvents: [ + // { + // eventName: "change", + // selector: "input", + // wrapperFunction: async (event) => { + // let url = event.currentTarget.value; + // let valid = HelperFunctions.manager.validateInput(url, "image"); + // if (valid) { + // await ImageDisplayManager.updateTileObjectTexture( + // tileID, + // frameTileID, + // url, + // "anyTile" + // ); + // } else { + // ui.notifications.error("URL not an image"); + // //TODO: show error? + // } + // }, + // }, + // ], + // }; + + // let popover = await Popover.processPopoverData( + // event.currentTarget, + // app.element, + // templateData, + // elementData + // ); + // popover[0].querySelector("input").focus({ focusVisible: true }); + // }, + // }, + // showArtSheets: {}, + createNewTileData: { + icon: "fas fa-plus", + tooltipText: "Create new tile data", + renderedInTemplate: true, + onClick: async (event, options) => + await extraActions.updateTileData(event, options), + }, + }, + }, + change: { + propertyString: "globalActions.change.actions", + actions: { + setArtScene: { + onChange: async (event, options = {}) => { + let { app } = options; + let value = event.currentTarget.value; + await HelperFunctions.setSettingValue( + "artGallerySettings", + value, + "dedicatedDisplayData.scene.value" + ); + await app.renderWithData(); + }, + }, + setArtJournal: { + onChange: async (event, options = {}) => { + let { app } = options; + let value = event.currentTarget.value; + await HelperFunctions.setSettingValue( + "artGallerySettings", + value, + "dedicatedDisplayData.journal.value" + ); + await app.renderWithData(); + }, + }, + }, + }, + hover: { + actions: {}, + }, + }, + itemActions: { + change: { + propertyString: "itemActions.change.actions", + actions: { + setLinkedTile: { + onChange: async (event, options = {}) => { + let { app, tileID, targetElement } = options; + if (!targetElement) targetElement = event.currentTarget; + if (!app) app = game.JTCSlideshowConfig; + + let selectedID = targetElement.value; + await ArtTileManager.updateTileDataID(tileID, selectedID); + if (app.rendered) { + await app.renderWithData(); + } + }, + }, + setFrameTile: { + onChange: async (event, options) => + await extraActions.updateTileData(event, options), + }, + setDisplayName: { + onChange: async (event, options) => { + //validate the input first + let isValid = await UIA.validateInput(event, { + noWhitespaceStart: { + notificationType: "danger", + message: + "Please enter a name that doesn't start with a white space", + }, + }); + if (isValid) { + await extraActions.updateTileData(event, options); + } + }, + }, + shareURL: { + onChange: async (event, options = {}) => { + const { tileID, parentLI } = options; + const frameTileID = parentLI.dataset.frameId; + const url = event.currentTarget.value; + const valid = HelperFunctions.validateInput(url, "image"); + if (valid) { + await ImageDisplayManager.updateTileObjectTexture( + tileID, + frameTileID, + url, + "anyScene" + ); + } else { + ui.notifications.error("URL not an image"); + } + }, + }, + }, + }, + hover: { + propertyString: "itemActions.hover.actions", + actions: { + highlightTile: { + onHover: async (event, options = {}) => { + let isLeave = + event.type === "mouseout" || event.type === "mouseleave"; + let { targetElement } = options; + if (!targetElement) targetElement = event.currentTarget; + if (targetElement.tagName === "LABEL") { + targetElement = targetElement.previousElementSibling; + } + let tileID = targetElement.dataset.id; + let tile = await ArtTileManager.getTileObjectByID(tileID); + if (!isLeave) { + await game.JTCS.indicatorUtils.showTileIndicator(tile); + } else { + await game.JTCS.indicatorUtils.hideTileIndicator(tile); + } + }, + }, + highlightItemAndTile: { + //TODO: refactor the name of this property to include the "showInstructions" method + onHover: async (event, options = {}) => { + const dataset = $(options.parentLI).data(); + await extraActions.highlightItemAndTile(event, { + ...options, + ...dataset, + }); + await extraActions.showInstructions(event, { + ...options, + ...dataset, + }); + }, + }, + }, + }, + click: { + //for buttons + propertyString: "itemActions.click.actions", + actions: { + setAsDefault: { + onClick: async (event, options = {}) => { + await extraActions.setDefaultTileInScene(event, options); + }, + renderNever: true, + }, + shareURLOnTile: { + icon: "fas fa-external-link-alt", + tooltipText: "Share image from a url on this tile", + onClick: async (event, options = {}) => { + let { tileID, parentLI, app, html } = options; + let frameTileID = parentLI.dataset.frameId; + let context = { + name: "shareUrl", + id: "shareURL", + changeAction: "itemActions.change.actions.shareURL", + label: "Share URL", + }; + + let templateData = Popover.createTemplateData( + parentLI, + "input-with-error", + context + ); + + let elementData = { ...Popover.defaultElementData }; + + elementData["popoverElement"] = { + targetElement: null, + // hideEvents: [ + // { + // eventName: "change", + // selector: "input", + // wrapperFunction: async (event) => { + // let url = event.currentTarget.value; + // let valid = HelperFunctions.manager.validateInput(url, "image"); + // if (valid) { + // await ImageDisplayManager.updateTileObjectTexture( + // tileID, + // frameTileID, + // url, + // "anyScene" + // ); + // } else { + // ui.notifications.error("URL not an image"); + // } + // }, + // }, + // ], + }; + + let popover = await Popover.processPopoverData( + // event.currentTarget, + event.target, + app.element, + templateData, + elementData + ); + popover[0].querySelector("input").focus({ focusVisible: true }); + await app.activateListeners(app.element); + }, + overflow: false, + artTileOnly: true, + }, + createNewGalleryTile: { + icon: "fas fa-plus", + tooltipText: + "Create a new tile object on the canvas in this scene, linked to this art gallery item", + overflow: false, + renderOnMissing: true, + onClick: async (event, options = {}) => { + let { tileID, parentLI, app } = options; + let isFrameTile = parentLI.dataset.type === "frame"; + await ArtTileManager.createAndLinkSceneTile({ + unlinkedDataID: tileID, + isFrameTile: isFrameTile, + }); + await app.renderWithData(); + }, + }, + showUnlinkedTiles: { + icon: "fas fa-link", + tooltipText: + "Show tiles objects on canvas that aren't linked to any art or frame tile data", + onClick: async (event, options = {}) => { + let { app, tileID, parentLI } = options; + + let frameTileID; + if (!frameTileID) frameTileID = parentLI.dataset.frameId; + + let artTileDataArray = + await ArtTileManager.getSceneSlideshowTiles("", true); + let unlinkedTilesIDs = await ArtTileManager.getUnlinkedTileIDs( + artTileDataArray + ); + let context = { + artTileDataArray: artTileDataArray, + unlinkedTilesIDs: unlinkedTilesIDs, + }; + let templateData = Popover.createTemplateData( + parentLI, + "tile-link-partial", + context + ); + + let elementData = { ...Popover.defaultElementData }; + // elementData["popoverElement"].hideEvents.push({ + // eventName: "change", + // selector: "input, input + label", + // wrapperFunction: async (event) => {}, + // }); + // -- RENDER THE POPOVER + let popover = await Popover.processPopoverData( + event.target, + app.element, + templateData, + elementData + ); + await app.activateListeners(app.element); + popover[0].querySelector("input").focus({ focusVisible: true }); + + return; + }, + overflow: false, + renderOnMissing: true, + }, + toggleTilesOpacity: { + icon: "fas fa-clone", + tooltipText: "fade out other tiles in scene to better see this one", + onClick: async (event, options = {}) => + extraActions.toggleTilesOpacity(event, options), + v9Only: true, + }, + + // the overflow menu should be last + toggleOverflowMenu: { + icon: "fas fa-ellipsis-v", + tooltipText: "show menu of extra options for this art tile", + overflow: false, + renderAlways: true, + onClick: async (event, options = {}) => { + const { app, tileID, parentLI } = options; + if (!tileID) tileID = parentLI.dataset.tileID; + const type = parentLI.dataset.type; + const parentItemMissing = parentLI.dataset.missing ? true : false; + + const actions = + slideshowDefaultSettingsData.itemActions.click.actions; + + let overflowActions = {}; + for (let actionKey in actions) { + let { overflow, renderOnMissing, renderAlways, artTileOnly } = + actions[actionKey]; + if (!renderOnMissing) renderOnMissing = false; //if render on missing is undefined, set it to false + + const shouldRender = renderOnMissing === parentItemMissing; //fif the parent item's missing status and the button's conditional rendering status are equal + const mismatchedTypes = type === "frame" && artTileOnly; //if the item should only render on an art tile + + if ( + overflow && + (shouldRender || renderAlways) && + !mismatchedTypes + ) { + //if it's an action to renderon the overflow menu + overflowActions[actionKey] = actions[actionKey]; + } + } + const context = { + propertyString: "itemActions.click.actions", + items: overflowActions, + }; + let templateData = Popover.createTemplateData( + parentLI, + "item-menu", + context + ); + + const elementData = { ...Popover.defaultElementData }; + + let popover = await Popover.processPopoverData( + event.target, + app.element, + templateData, + elementData + ); + + await app.activateListeners(app.element); + popover.focus({ focusVisible: true }); + }, + }, + //overflow menu items + selectTile: { + text: "Select tile object", + tooltipText: "Select the tile object in this scene", + icon: "fas fa-vector-square", + overflow: true, + renderOnMissing: false, + onClick: async (event, options = {}) => + await extraActions.selectTile(event, options), + }, + fitTileToFrame: { + icon: "fas fa-expand", + tooltipText: "Fit this art tile to its frame", + onClick: async (event, options = {}) => { + const { parentLI, tileID } = options; + const { type, frameId } = $(parentLI).data(); + if (type === "art") { + let path; + if (game.version >= 10) { + path = game.scenes.viewed.tiles.get(tileID).texture.src; + } else { + path = game.scenes.viewed.tiles.get(tileID).data.img; + } + await ImageDisplayManager.updateTileObjectTexture( + tileID, + frameId, + path, + "anyScene" + ); + } + }, + overflow: true, + artTileOnly: true, + }, + renderTileConfig: { + text: "Render tile object config", + tooltipText: + "Render the config for the tile object linked to this tile", + icon: "fas fa-cog", + overflow: true, + onClick: async (event, options = {}) => + await extraActions.renderTileConfig(event, options), + renderOnMissing: false, + }, + clearTileImage: { + icon: "fas fa-times-circle", + text: "Clear Tile Image", + tooltipText: + "'Clear' this tile's image, or reset it to your chosen default", + overflow: true, + onClick: async (event, options = {}) => { + let { tileID } = options; + // let tileID = parentElement.dataset.id; + await ImageDisplayManager.clearTile(tileID); + }, + extraClass: "danger-text", + }, + // bringTileToFront: { + // text: "Bring tile to front", + // tooltipText: "Bring the linked tile to the front" + // icon: "fas fa-arrow-to-top", + // }, + deleteTileData: { + icon: "fas fa-trash", + tooltipText: `delete this" this.type "tile data?" "
" + "(this will not delete the tile object in the scene itself)`, + + overflow: true, + renderAlways: true, + extraClass: "danger-text", + onClick: async (event, options = {}) => + extraActions.deleteTileData(event, options), + }, + }, + }, + }, +}; diff --git a/scripts/data/Universal-Actions.js b/scripts/data/Universal-Actions.js new file mode 100644 index 0000000..92aa95a --- /dev/null +++ b/scripts/data/Universal-Actions.js @@ -0,0 +1,270 @@ +const validatorExpressions = { + noWhitespaceStart: /^[\S]+/, +}; +const notificationIcons = { + danger: "fas fa-exclamation-circle", + warning: "fas fa-exclamation-triangle", +}; + +/** + * Hide (or show) all of an element's sibling elements + * @param {event} event - the event that triggered this + */ +function toggleHideAllSiblings(event, currentTarget) { + if (!event && !currentTarget) return; + if (!currentTarget) currentTarget = event.currentTarget; + + const siblings = Array.from(currentTarget.parentNode.children).filter( + (item) => !item.isSameNode(currentTarget) + ); + + if (currentTarget.classList.contains("active")) { + siblings.forEach((el) => el.classList.remove("JTCS-hidden")); + } else { + siblings.forEach((el) => el.classList.add("JTCS-hidden")); + } +} + +function fadeSheetOpacity(event, selector = ".window-content") { + event.preventDefault(); + + const windowContent = event.currentTarget.closest(selector); + const faded = + windowContent.classList.contains("fade") || + windowContent.classList.contains("fade-all"); + + if (faded) { + windowContent.classList.remove("fade"); + } else { + windowContent.classList.add("fade"); + } +} +/** + * Insert a notification inline + * @param {*} event - the event that triggered this notification + * @param {*} ancestorSelector - the ancestor element into which we want to insert this notification element + * @param {Object} options - options to customize this notification + * @param {String} options.message - the notification message + * @param {String} options.notificationType - the type of notification, to affect its icon and styling + */ +async function renderInlineNotification( + event, + ancestorSelector = "formGroup", + options = {} +) { + let { notificationType, icon } = options; + if (!icon) { + if (notificationType) { + options.icon = notificationIcons[notificationType]; + } else { + notificationType = "error"; + options.icon = notificationIcons[notificationType]; + } + } + const parentItem = event.currentTarget.closest(`.${ancestorSelector}`); + let template = game.JTCS.templates["notification-badge"]; + let renderHTML = await renderTemplate(template, options); + parentItem.insertAdjacentHTML("beforeend", renderHTML); +} + +function setAnimDefaults(animOptions) { + const defaultOptions = { + isFadeOut: false, + duration: 300, + onFadeOut: ($el, event) => { + $el.remove(); + }, + }; + return mergeObject(defaultOptions, animOptions); +} +/** + * + * for fading objects in and out when they enter or exit the DOM + * @param {JQuery} $element - Jquery object representing element to fade + * @param {Object} options - the options object + * @param {Number} options.duration - default fade animation duration + * @param {Boolean} options.isFadeOut - a boolean determining whether or not this should fade in our out + * @param {Function} options.onFadeOut - the callback to handle what happens when the fade animation is complete + */ +async function fade($element, options = {}) { + const { duration, isFadeOut, onFadeOut, onCancel } = setAnimDefaults(options); + + if ($element.length === 0) return; + + let fadeAnim = $element[0].animate( + [ + // keyframes + { opacity: isFadeOut ? "100%" : "0%" }, + { opacity: isFadeOut ? "0%" : "100%" }, + ], + { + // timing options + duration: duration, + } + ); + fadeAnim.addEventListener("finish", (event) => { + if (isFadeOut) { + onFadeOut($element, event); + } + }); + fadeAnim.addEventListener("cancel", (event) => { + if (onCancel) { + onCancel($element, event); + } + }); + return fadeAnim; +} +/** + * Handle the adding and removal of classes and triggering of animations to hide and fade elements + * @param {JQuery} $element - the JQuery object representing DOM Element we want to show or hide + */ +async function handleVisibilityTransitions($element) { + //if the class already has hidden, set it to fadeIn rather than out + const isFadeOut = $element.hasClass("JTCS-hidden") ? false : true; + + //if we're fading in, remove the hidden class + if (!isFadeOut) $($element).removeClass("JTCS-hidden"); + + //set our fade animation options + let options = { + isFadeOut, + onFadeOut: ($element, event) => $element.addClass("JTCS-hidden"), + }; + //handle the fade animation + //? Fade will handle the opacity, while our "JTCS-hidden" class handles everything else (transform, clip rect, position absolute, etc.) + fade($($element), options); +} +function toggleActiveStyles(event, el, useInitialTarget = true) { + if (!el) { + el = event.currentTarget; + if (useInitialTarget) { + //use target instead of currentTarget + el = event.target; + } + } + + if (el.classList.contains("active")) { + el.classList.remove("active"); + } else { + el.classList.add("active"); + } +} +/** + * Turn off other elements that have active styles + * @param {HTMLEvent} event - the triggering event + * @param {Element} el - the element on which to apply the active styles + * @param {String} otherSelector - a selector to find other elements that are set to "active" + */ +function clearOtherActiveStyles(event, el, otherSelector, parentSelector) { + const parentItem = el.closest(parentSelector); + let others = Array.from(parentItem.querySelectorAll(otherSelector)).filter( + (item) => !item.isSameNode(el) + ); + others = others.filter((other) => other.classList.contains("active")); + + others.forEach((other) => toggleActiveStyles(event, other)); +} +export const universalInterfaceActions = { + /** + * + * Show or hide another element + * @param {HTMLEvent} event - the event that provoked this + * @param {Object} options - options object + * @param {HTMLElement} options.parentItem - the parent item in which to find the element we want to hide/show + * @param {String} options.targetClassSelector - the class of the item we want to show + */ + toggleShowAnotherElement: (event, options) => { + let { parentItem, targetClassSelector, fadeIn = true } = options; + let el = event.currentTarget; + let targetID = el?.dataset.targetId; + let target; + if (targetID) { + target = parentItem.querySelector(`#${targetID}`); + } else { + target = parentItem.querySelector(`.${targetClassSelector}`); + } + if (target) { + if (fadeIn) { + handleVisibilityTransitions($(target)); + } else { + $(target).removeClass("JTCS-hidden"); + } + } + }, + toggleActiveStyles: toggleActiveStyles, + clearOtherActiveStyles: clearOtherActiveStyles, + fadeSheetOpacity: fadeSheetOpacity, + toggleHideSelf: (event) => { + let el = event.currentTarget; + el.classList.toggle("JTCS-hidden"); + }, + toggleHideAncestor: (event, options) => { + let { ancestorSelector } = options; + let el = event.currentTarget; + el.closest(ancestorSelector).classList.toggle("JTCS-hidden"); + // parentItem.classList.toggle("JTCS-hidden"); + }, + toggleHideAllSiblings, + scrollOtherElementIntoView: (event, options) => { + const { parentItem: $parentItem } = options; + let currentTarget = event.currentTarget; + let scrollTargetID = currentTarget.dataset.target; + let scrollTarget = $parentItem.find(`#${scrollTargetID}`); + scrollTarget[0].scrollIntoView(); + clearOtherActiveStyles( + event, + currentTarget, + "[data-action='scrollTo']", + "#JTCSsettingsHeader" + ); + toggleActiveStyles(event, currentTarget); + }, + renderAnotherApp: async (appName, constructor) => { + //if global variable's not initialized, initialize it + if (!game[appName]) game[appName] = new constructor(); + //if it's not rendered, render it + if (!game[appName].rendered) { + await game[appName].render(true); + // window[appName] = constructor; + } else { + //if it is rendered, bring it to the top + await game[appName].bringToTop(); + } + }, + renderInlineNotification: renderInlineNotification, + fade, + + validateInput: async ( + event, + validators = { + noWhitespaceStart: { + notificationType: "error", + message: "Please enter a value that doesn't start with a white space", + }, + } + ) => { + const { value } = event.currentTarget; + const validatorKeys = Object.keys(validators); + let allValid = true; + /// do validation here + let firstInvalidObject = {}; + validatorKeys.forEach((key) => { + const regexp = validatorExpressions[key]; + let isValid = regexp.test(value); + if (!isValid) { + //if one expression doesn't match + //set the full "allValid" boolean to false + allValid = false; + firstInvalidObject = validators[key]; + let { notificationType: type } = firstInvalidObject; + firstInvalidObject.icon = notificationIcons[type]; + } + }); + + //if one of the validators returns invalid, show a notification + if (!allValid) { + await renderInlineNotification(event, "form-group", firstInvalidObject); + } + return allValid; + }, +}; diff --git a/scripts/data/input-validators.js b/scripts/data/input-validators.js new file mode 100644 index 0000000..7763924 --- /dev/null +++ b/scripts/data/input-validators.js @@ -0,0 +1,4 @@ +const notificationIcons = { + error: "fas fa-exclamation-circle", + warning: "fas fa-exclamation-triangle", +}; diff --git a/scripts/data/templates.js b/scripts/data/templates.js new file mode 100644 index 0000000..4f17f69 --- /dev/null +++ b/scripts/data/templates.js @@ -0,0 +1,54 @@ +import { MODULE_ID } from "../debug-mode.js"; + +const baseTemplatePath = `modules/${MODULE_ID}/templates/`; + +const templateBaseNames = [ + `tile-list-item.hbs`, + "tooltip.hbs", + "control-button.hbs", + `new-tile-list-item.hbs`, + `icon-button.hbs`, + `display-tile-config.hbs`, + `image-controls.hbs`, + `tile-link-partial.hbs`, + `input-with-error.hbs`, + `share-url-partial.hbs`, + `fieldset-button-group.hbs`, + `checkbox-chip-group.hbs`, + `notification-badge.hbs`, + `popover.hbs`, + `item-list.hbs`, + `item-menu.hbs`, + `sheet-wide-controls.hbs`, + `badge.hbs`, + `delete-confirmation-prompt.hbs`, + `scene-tile-wrapper.hbs`, + `color-picker.hbs`, + `checkbox.hbs`, +]; +/** + * @param {*} templateBaseNameArray + * @returns + */ +export function generateTemplates() { + let templates = templateBaseNames.map((baseName) => + createTemplatePathString(baseName) + ); + return templates; +} +export function createTemplatePathString(templateBaseName) { + return `${baseTemplatePath}${templateBaseName}`; +} +export function mapTemplates(templates) { + if (!templates) { + templates = generateTemplates(); + } + let mappedTemplates = {}; + + templates.forEach((path) => { + let baseName = path.split("/").pop().split(".").shift(); + mappedTemplates[baseName] = path; + }); + + return mappedTemplates; +} diff --git a/scripts/debug-mode.js b/scripts/debug-mode.js new file mode 100644 index 0000000..6e2345d --- /dev/null +++ b/scripts/debug-mode.js @@ -0,0 +1,50 @@ +export const MODULE_ID = "journal-to-canvas-slideshow"; +import { SlideshowConfig } from "./SlideshowConfig.js"; +// import { JTCSSettingsApplication } from "./classes/JTCSSettingsApplication.js"; + +Hooks.once("devModeReady", ({ registerPackageDebugFlag }) => { + registerPackageDebugFlag(MODULE_ID); +}); + +// Hooks.on("canvasReady", ({}) => { +// const isDebugging = game.modules.get("_dev-mode")?.api?.getPackageDebugValue(MODULE_ID); +// //re-render the tile config +// const autoRenderDocs = { +// // journal: ["Art"], +// }; +// const appNames = { +// JTCSlideshowConfig: new SlideshowConfig(), + +// // JTCSSettingsApp: new JTCSSettingsApplication(), +// }; +// if (game.user.isGM && isDebugging) { +// Object.keys(appNames).forEach((key) => { +// autoRenderApp(key, appNames[key]); +// }); +// Object.keys(autoRenderDocs).forEach((collectionName) => { +// autoRenderDocs[collectionName].forEach((name) => { +// game[collectionName].getName(name).sheet.render(true); +// }); +// }); + +// // if (!game[appName]) game[appName] = appNames[appName]; //.render(true) +// // game[appName].render(true); +// // game.journal.getName("Art").sheet.render(true); +// } +// }); +function autoRenderApp(appName, instance) { + if (!game[appName]) game[appName] = instance; + game[appName].render(true); +} + +export function log(force, ...args) { + try { + const isDebugging = game.modules.get("_dev-mode")?.api?.getPackageDebugValue(MODULE_ID); + + if (force || isDebugging) { + console.log(MODULE_ID, "|", ...args); + } + } catch (e) {} +} + +// ... diff --git a/scripts/handlebars/register-helpers.js b/scripts/handlebars/register-helpers.js new file mode 100644 index 0000000..ef93068 --- /dev/null +++ b/scripts/handlebars/register-helpers.js @@ -0,0 +1,111 @@ +import { HelperFunctions } from "../classes/HelperFunctions.js"; +export const registerHelpers = function () { + Handlebars.registerHelper("flattenObject", (object) => { + return flattenObject(object); + }); + + /** + * Returns a property string that matches a dot-notation-chain for nested objects + */ + Handlebars.registerHelper("getPropertyString", (rootObject, parentKey, childKey) => { + const parentObject = getProperty(rootObject, parentKey); + const flat = flattenObject(parentObject); + const newFlat = Object.keys(flat).map((key) => `${parentKey}.${key}`); + return newFlat.find((key) => key.includes(childKey)); + }); + + Handlebars.registerHelper("applyTemplate", function (subTemplateId, context) { + var subTemplate = Handlebars.compile($("#" + subTemplateId).html()); + var innerContent = context.fn({}); + var subTemplateArgs = _.extend({}, context.hash, { + content: new Handlebars.SafeString(innerContent), + }); + + return subTemplate(subTemplateArgs); + }); + Handlebars.registerHelper( + "checkAll", + function (anyOrAll = "all", valueToEqual = true, ...conditions) { + conditions.pop(); + //if the property has every object, and every object is true + + if (anyOrAll === "all") { + return conditions.every((condition) => { + return condition === valueToEqual; + }); + } else { + return conditions.some((condition) => { + return condition === valueToEqual; + }); + } + } + ); + Handlebars.registerHelper("filter", function (object, conditionName, conditionValue) { + let array = Object.entries(object).filter( + ([key, data]) => data[conditionName] === conditionValue || data.renderAlways + ); + return Object.fromEntries(array); + }); + Handlebars.registerHelper("camelCaseToDashCase", function (string) { + let dashedString = string.replace(/[A-Z]/g, (m) => "-" + m.toLowerCase()); + return dashedString; + }); + Handlebars.registerHelper("safeString", function (string) { + return Handlebars.SafeString(string); + }); + Handlebars.registerHelper("camelCaseToCapitalString", function (string) { + return HelperFunctions.capitalizeEachWord(string, ""); + // let sentence = string.split(/(?=[A-Z])/).map((s) => s.toLowerCase()); + // sentence = sentence.map((s) => s.charAt(0).toUpperCase() + s.slice(1)); + // sentence = sentence.join(" "); + // return sentence; + }); + + Handlebars.registerHelper("camelCaseToArray", function (string, shouldJoin = false) { + let sentence = string.split(/(?=[A-Z])/).map((s) => s.toLowerCase()); + if (shouldJoin) { + sentence = sentence.join(" "); + } + return sentence; + }); + + Handlebars.registerHelper("combineToString", function (...args) { + args.pop(); + let sentence = args.join(" "); + return new Handlebars.SafeString(sentence); + }); + + Handlebars.registerHelper( + "wrapInSpan", + function (stringToWrap, classList = "accent") { + let wrappedString = `${stringToWrap}`; + return new Handlebars.SafeString(wrappedString); + } + ); + + Handlebars.registerHelper( + "wrapInElement", + function (stringToWrap, tagName, classList = "") { + let wrappedString = `<${tagName} class=${classList}>${stringToWrap}`; + return new Handlebars.SafeString(wrappedString); + } + ); + + Handlebars.registerHelper("generateChildPartials", function (object) {}); + + Handlebars.registerHelper("ternary", function (test, yes, no) { + return test ? yes : no; + }); + + Handlebars.registerHelper("dynamicPartial", function (key, partials = "") { + if (!partials || !Array.isArray(partials)) { + partials = game.JTCS.templates; + } + let partialPath = partials[key]; + return new Handlebars.SafeString(partialPath); + }); + + Handlebars.registerHelper("withTooltip", function () { + let wrappedString = `${stringToWrap}`; + }); +}; diff --git a/scripts/handlebars/register-partials.js b/scripts/handlebars/register-partials.js new file mode 100644 index 0000000..21d4a1e --- /dev/null +++ b/scripts/handlebars/register-partials.js @@ -0,0 +1,16 @@ +const baseTemplatePath = `modules/${MODULE_ID}/templates/`; + +const templateBaseNames = [ + `tile-list-item.hbs`, + "tooltip.hbs", + "control-button.hbs", + `new-tile-list-item.hbs`, + `icon-button.hbs`, + `display-tile-config.hbs`, + `image-controls.hbs`, + `tile-link-partial.hbs`, +]; +export const registerPartials = function () { + let templates = generateTemplates(templateBaseNames); + loadTemplates(templates); +}; diff --git a/scripts/hooks.js b/scripts/hooks.js new file mode 100644 index 0000000..7c559f6 --- /dev/null +++ b/scripts/hooks.js @@ -0,0 +1,217 @@ +import { JTCSModules } from "./init.js"; +import { HelperFunctions } from "./classes/HelperFunctions.js"; +import { ArtTileManager } from "./classes/ArtTileManager.js"; +/** + * This sets up most hooks we want to respond to in our code, + * grouping hooks with identical + * callbacks into arrays on an object + * @returns object containing registerHooks function + */ +export const setupHookHandlers = async () => { + async function renderSlideshowConfig(...args) { + if (args[2]?.diff && args[1]?.alpha) { + //TODO: ? this was a workaround for v10, keeping Scene Gallery Config from re-rendering on update of tile alpha, but should remove + //don't update if the changes include alpha + return; + } + if (game.JTCSlideshowConfig && game.JTCSlideshowConfig.rendered) { + game.JTCSlideshowConfig.render(false); + } + } + + /** + * Show a toggle in the journal sheet's header to toggle whether the journal + * has controls on or off + */ + + async function renderImageControls(app, html) { + if (!game.user.isGM) { + return; + } + + await JTCSModules.SheetImageApp.applyImageClasses(app, html); + } + + async function updateGalleryTileIndicator(tileDoc) { + let tileID = tileDoc.id; //this gets the id from the tile's document itself + let sceneGalleryTiles = await JTCSModules.ArtTileManager.getSceneSlideshowTiles( + "", + true + ); + let foundTileData = await JTCSModules.ArtTileManager.getTileDataFromFlag( + tileID, + sceneGalleryTiles + ); //this is looking for tiles that are already linked + + await JTCSModules.CanvasIndicators.setUpIndicators(foundTileData, tileDoc); + } + + async function updateAllGalleryIndicators(scene) { + let tiles = scene.tiles; + let artTileDataArray = await JTCSModules.ArtTileManager.getSceneSlideshowTiles( + "", + true + ); + tiles.forEach(async (tileDoc) => { + let foundTileData = artTileDataArray.find( + (tileData) => tileData.id === tileDoc.id + ); + await JTCSModules.CanvasIndicators.setUpIndicators(foundTileData, tileDoc); + }); + } + + async function addJTCSControls(controls) { + if (!game.user.isGM) { + return; + } + const tileControls = controls.find((control) => control?.name === "tiles"); + + tileControls.tools.push({ + name: "ShowJTCSConfig", + title: "Show Slideshow Config", + icon: "far fa-image", + onClick: () => { + new JTCSModules.SlideshowConfig().render(true); + }, + button: true, + }); + } + /** + * Re render the image sheet for fresh controls whenever the JTCSSettings, or the SlideshowConfig data for the current scene (individual tile data or the default tile, for instance) is updated + * @param {Object} options - option arguments to be passed to this + * @param {String} options.origin - the origin of the Hook - was it the settings, or a flag update for the current scene's slideshow settings? + * @param {Object} options.currentScene - the scene, usually the current scene + * @param {String} options.tileID - the ID of the tile, if updated + */ + function rerenderImageSheet(options) { + const { origin, currentScene, updateData } = options; + + let renderedSheets = Object.values(window.ui.windows).filter( + (item) => item.document?.documentName + ); + renderedSheets.forEach((sheet) => { + const docType = sheet.document.documentName.toLowerCase(); + + //if our type of document is set to "true" as rendering controls in the settings + //and it's not currently being edited + //and we're not telling it to render anyway + let editorsActive = HelperFunctions.editorsActive(sheet); + if (editorsActive !== true) { + sheet.render(); + } + }); + } + + const hookHandlers = { + rerenderImageSheet: { + //when the art gallery tiles update, re-render the sheets + hooks: [ + "updateArtGalleryTiles", + "updateDefaultArtTile", + "updateJTCSSettings", + "canvasReady", + ], + handlerFunction: rerenderImageSheet, + }, + renderImageControls: { + hooks: [ + "renderItemSheet", + "renderActorSheet", + "renderJournalSheet", + "renderJournalPageSheet", + "update", + ], + // hooks: ["renderJournalSheet"], + handlerFunction: renderImageControls, + }, + renderSlideshowConfig: { + hooks: [ + "createTile", + // "updateTile", + "preUpdateTile", + "deleteTile", + "canvasReady", + "createJournalEntry", + "updateJournalEntry", + "deleteJournalEntry", + "updateJTCSSettings", + // "updateArtGalleryTiles" + "updateDefaultArtTile", + ], + handlerFunction: renderSlideshowConfig, + }, + updateCanvasIndicators: { + hooks: ["createTile", "updateTile", "deleteTile"], + handlerFunction: updateGalleryTileIndicator, + specialHooks: [ + { + hookName: "canvasReady", + handlerFunction: async (canvas) => { + updateAllGalleryIndicators(canvas.scene); + }, + }, + { + hookName: "updateJTCSSettings", + handlerFunction: async (options) => { + // let { currentScene } = options; + let currentScene = game.scenes.viewed; + await updateAllGalleryIndicators(currentScene); + }, + }, + { + hookName: "updateArtGalleryTiles", + handlerFunction: async (options) => { + // let { currentScene } = options; + let currentScene = game.scenes.viewed; + await updateAllGalleryIndicators(currentScene); + }, + }, + { + hookName: "updateDefaultArtTile", + handlerFunction: async (options) => { + let currentScene = game.scenes.viewed; + await updateAllGalleryIndicators(currentScene); + }, + }, + ], + }, + addJTCSControls: { + hooks: ["getSceneControlButtons"], + handlerFunction: addJTCSControls, + }, + updateUIColors: { + hooks: ["updateJTCSSettings"], + handlerFunction: async () => { + await HelperFunctions.setUIColors(); + }, + }, + updateDefaultArtTile: { + hooks: ["deleteTile"], + handlerFunction: async (tileDoc) => { + // let tiles = (await ArtTileManager.getSceneSlideshowTiles("art", true)).filter((item)=> !item.missing) + // if(tiles){ + // } + // await ArtTileManager.updateDefaultArtTile(tileDoc); + }, + }, + }; + + async function registerHooks() { + for (let handlerKey in hookHandlers) { + let handler = hookHandlers[handlerKey]; + if (handler.specialHooks) { + handler.specialHooks.forEach((specialHookData) => { + let { hookName, handlerFunction: callback } = specialHookData; + Hooks.on(hookName, callback); + }); + } + for (let hookName of handler.hooks) { + Hooks.on(hookName, handler.handlerFunction); + // Hooks.once(hookName, handler.handlerFunction); + } + } + } + return { + registerHooks: registerHooks, + }; +}; diff --git a/scripts/init.js b/scripts/init.js new file mode 100644 index 0000000..23c5c66 --- /dev/null +++ b/scripts/init.js @@ -0,0 +1,244 @@ +"use strict"; +import { ArtTileManager } from "./classes/ArtTileManager.js"; +import { CanvasIndicators } from "./classes/CanvasIndicators.js"; +import { HelperFunctions } from "./classes/HelperFunctions.js"; +import { ImageDisplayManager } from "./classes/ImageDisplayManager.js"; +import ImageVideoPopout from "./classes/MultiMediaPopout.js"; +import { JTCSActions } from "./data/JTCS-Actions.js"; +import { SheetImageApp } from "./SheetImageApp.js"; +import { SheetImageDataController } from "./SheetImageDataController.js"; +import { SlideshowConfig } from "./SlideshowConfig.js"; + +import { log, MODULE_ID } from "./debug-mode.js"; +import { + generateTemplates, + createTemplatePathString, + mapTemplates, +} from "./data/templates.js"; +import { registerHelpers } from "./handlebars/register-helpers.js"; +import { registerSettings } from "./settings.js"; +import { setupHookHandlers } from "./hooks.js"; +import { universalInterfaceActions as UIA } from "./data/Universal-Actions.js"; + +export const JTCSModules = { + ArtTileManager, + CanvasIndicators, + HelperFunctions, + ImageDisplayManager, + ImageVideoPopout, + JTCSActions, + SheetImageApp, + SheetImageDataController, + SlideshowConfig, +}; + +Hooks.once("ready", () => { + try { + window.Ardittristan.ColorSetting.tester; + } catch { + ui.notifications.notify( + 'Journal to Canvas Slideshow requires the "lib - ColorSettings" module. Please make sure you have it installed and enabled.', + "error" + ); + } +}); + +Hooks.on("init", async () => { + console.log("Initializing Journal to Canvas Slideshow"); + + //register settings + registerSettings(); + //register handlebars helpers + registerHelpers(); + + libWrapper.register( + "journal-to-canvas-slideshow", + "TextEditor._onDropEditorData", + function (wrapped, ...args) { + let event = args[0]; + let editor = args[1]; + var files = event.dataTransfer.files; + let containsImage = false; + for (let f of files) { + let type = f["type"].split("/")[0]; + if (type === "image") { + containsImage = true; + insertImageIntoJournal(f, editor); + } + } + if (!containsImage) { + console.log("TextEditor._onDropEditorData called"); + return wrapped(...args); + } + }, + "MIXED" + ); + + //map of template names w/ short keys + let templates = generateTemplates(); + let mappedTemplates = mapTemplates(templates); + + // once settings are set up, create our API object + game.modules.get("journal-to-canvas-slideshow").api = { + templates: mappedTemplates, + imageUtils: { + manager: ImageDisplayManager, + displayImageInScene: ImageDisplayManager.displayImageInScene, + updateTileObjectTexture: ImageDisplayManager.updateTileObjectTexture, + scaleToScene: ImageDisplayManager.scaleArtTileToScene, + scaleToBoundingTile: ImageDisplayManager.scaleArtTileToFrameTile, + }, + tileUtils: { + manager: ArtTileManager, + getLinkedFrameID: ArtTileManager.getLinkedFrameID, + createAndLinkSceneTile: ArtTileManager.createAndLinkSceneTile, + // createArtTileObject: ArtTileManager.createArtTileObject, + // createFrameTileObject: ArtTileManager.createFrameTileObject, + getSceneSlideshowTiles: ArtTileManager.getSceneSlideshowTiles, + getDefaultData: ArtTileManager.getDefaultData, + getFrameTiles: ArtTileManager.getFrameTiles, + getDisplayTiles: ArtTileManager.getDisplayTiles, + getTileObjectByID: ArtTileManager.getTileObjectByID, + selectTile: ArtTileManager.selectTile, + renderTileConfig: ArtTileManager.renderTileConfig, + updateTileDataID: ArtTileManager.updateTileDataID, + updateSceneTileFlags: ArtTileManager.updateSceneTileFlags, + getTileDataFromFlag: ArtTileManager.getTileDataFromFlag, + getUnlinkedTileIDs: ArtTileManager.getUnlinkedTileIDs, + getMissingTiles: ArtTileManager.getMissingTiles, + deleteSceneTileData: ArtTileManager.deleteSceneTileData, + getAllScenesWithSlideshowData: ArtTileManager.getAllScenesWithSlideshowData, + }, + indicatorUtils: { + createTileIndicator: CanvasIndicators.createTileIndicator, + deleteTileIndicator: CanvasIndicators.deleteTileIndicator, + hideTileIndicator: CanvasIndicators.hideTileIndicator, + showTileIndicator: CanvasIndicators.showTileIndicator, + setUpIndicators: CanvasIndicators.setUpIndicators, + }, + utils: { + manager: HelperFunctions, + createDialog: HelperFunctions.createDialog, + swapTools: HelperFunctions.swapTools, + setSettingValue: HelperFunctions.setSettingValue, + getSettingValue: HelperFunctions.getSettingValue, + createTemplatePathString: createTemplatePathString, + createEventActionObject: HelperFunctions.createEventActionObject, + }, + sheetImageUtils: { + manager: SheetImageApp, + }, + }; + + //load templates + loadTemplates(templates); + // now that we've created our API, inform other modules we are ready + // provide a reference to the module api as the hook arguments for good measure + Hooks.callAll( + "journalToCanvasSlideshowReady", + game.modules.get("journal-to-canvas-slideshow").api + ); +}); + +Hooks.on("journalToCanvasSlideshowReady", async (api) => { + game.JTCS = api; + await (await setupHookHandlers()).registerHooks(); + await HelperFunctions.setUIColors(); + // await setupHookHandlers.registerHooks(); + // setUpSheetRenderHooks(); + // setUpIndicatorHooks(); +}); + +Hooks.once("renderSlideshowConfig", (app) => { + game.JTCSlideshowConfig = app; +}); + +async function insertImageIntoJournal(file, editor) { + if (typeof ForgeVTT != "undefined" && ForgeVTT.usingTheForge) { + source = "forgevtt"; + } else { + var source = "data"; + } + let response; + if (file.isExternalUrl) { + response = { + path: file.url, + }; + } else { + response = await FilePicker.upload( + source, + "/upload", + // game.settings.get("journal-to-canvas-slideshow", "imageSaveLocation"), + file, + {} + ); + } + let contentToInsert = `

`; + if (contentToInsert) editor.insertContent(contentToInsert); +} + +Hooks.once("canvasReady", async () => { + const showWelcomeMessage = await HelperFunctions.getSettingValue( + "showWelcomeMessage" + ); + if (showWelcomeMessage && game.user.isGM) { + await HelperFunctions.showWelcomeMessage(); + } +}); + +Hooks.on("canvasReady", async (canvas) => { + //get tile data from scene flags + + let artTileDataArray = (await ArtTileManager.getSceneSlideshowTiles("", true)).filter( + (item) => !item.missing + ); + + game.scenes.viewed.tiles.forEach(async (tileDoc) => { + let foundTileData = artTileDataArray.find( + (tileData) => tileData.id === tileDoc.id + ); + + await CanvasIndicators.setUpIndicators(foundTileData, tileDoc); + }); +}); + +/** + * For rendering a button in the Tile Config that shows the linked Art Tile + */ +Hooks.on("renderTileConfig", async (app, element) => { + let currentScene = game.scenes.viewed; + + //get tiles with flags + let flaggedTiles = await ArtTileManager.getSceneSlideshowTiles(); + let defaultData = await ArtTileManager.getDefaultData(); + + //get data from tiles + if (flaggedTiles) { + let tileID = game.version >= 10 ? app.object._id : app.object.data._id; + defaultData = await ArtTileManager.getTileDataFromFlag(tileID, flaggedTiles); + defaultData = { ...defaultData }; + defaultData.boundingTiles = await game.JTCS.tileUtils.getFrameTiles(flaggedTiles); + } + + if (element[0] && !element[0]?.querySelector("#displayTileData")) { + //if we don't have this data + // let template = "modules/journal-to-canvas-slideshow/templates/display-tile-config.hbs"; + const showConfigButton = document.createElement("button"); + showConfigButton.textContent = "Open Gallery Config"; + showConfigButton.setAttribute("id", defaultData.id); + showConfigButton.setAttribute("type", "button"); + + const target = $(element).find(`[name="tint"]`).parent().parent(); + target.after(showConfigButton); + $(showConfigButton).on("click", async (event) => { + event.preventDefault(); + await UIA.renderAnotherApp("JTCSlideshowConfig", SlideshowConfig); + // let btn = event.currentTarget; + // if (game.JTCSlideshowConfig) { + // game.JTCSlideshowConfig.render(true); + // } else { + // game.JTCSlideshowConfig = new SlideshowConfig().render(true); + // } + }); + } +}); diff --git a/scripts/settings.js b/scripts/settings.js new file mode 100644 index 0000000..95d0c72 --- /dev/null +++ b/scripts/settings.js @@ -0,0 +1,189 @@ +"use strict"; +import { MODULE_ID, log } from "./debug-mode.js"; +import { JTCSSettingsApplication } from "./classes/JTCSSettingsApplication.js"; +import { HelperFunctions } from "./classes/HelperFunctions.js"; +const assetFolderBasePath = `modules/${MODULE_ID}/assets/`; +export const colorThemes = { + dark: { + colorSchemeData: { + colors: { + backgroundColor: "#010527", + accentColor: "#9876ff", + }, + }, + indicatorColorData: { + colors: { + frameTileColor: "#2ec4b6", + unlinkedTileColor: "#ff5369", + artTileColor: "#ff9f1c", + defaultTileColor: "#ff60f4", + }, + }, + }, + light: { + colorSchemeData: { + colors: { + accentColor: "#582eff", + backgroundColor: "#ffffff", + }, + }, + indicatorColorData: { + colors: { + frameTileColor: "#b44b00", + artTileColor: "#009ec5", + unlinkedTileColor: "#33ac4b", + defaultTileColor: "#df00b2", + }, + }, + }, +}; +export const artGalleryDefaultSettings = { + sheetSettings: { + name: "Sheet Types", + + hint: `Which types of sheets would you like to show clickable image controls?

+ Note: These options determine if these sheet types have the controls visible by default. You'll still be able to toggle controls on and off on each individual sheet regardless. `, + modularChoices: { + journalEntry: true, + actor: true, + item: true, + }, + }, + colorSchemeData: { + theme: "light", + name: "Custom Color Scheme", + hint: `What colors would you like to use on parts of the JTCS UI? This will affect things like buttons, checkboxes, borders, etc. +

Hint: Click 'Apply Changes' to refresh this window and immediately see how your chosen colors look. + `, + colors: { + backgroundColor: "#010527", + accentColor: "#9876ff", + }, + propertyNames: { + accentColor: "--JTCS-accent-color", + backgroundColor: "--JTCS-background-color", + }, + colorVariations: { + accentColor: true, + backgroundColor: true, + }, + autoContrast: true, + }, + dedicatedDisplayData: { + name: "Art Journal and Art Scene", + hint: `Select your Art Journal and Art Scene. + These are "dedicated" displays, meaning if you choose the "Art Journal" or "Art Scene" actions on the sheet image controls or within the URL Share Dialog, + images will be sent directly to that scene or journal. +

+ View the Features and Walkthrough document for a demonstration and More Info + .`, + journal: { + name: "Art Journal", + value: "", + hint: `Select your Art Journal, then choose additional functionality for what automatically happens when the image is changed +

Auto Activate - will automatically show the Journal Entry to you and all of your players +

Auto View - will render the journal entry for you but not your players (useful if you wish to check that the image properly updated) + `, + autoActivate: false, + autoView: false, + }, + scene: { + name: "Art Scene", + value: "", + hint: `Select your Art Scene, then choose additional functionality for what automatically happens when the image is changed: +

Auto Activate - will automatically activate the scene for you and all of your players +

Auto View - will automatically view the scene for you (useful if you wish to check that the default tile image actually updated) +

Note: only scenes with a Default Art Tile will be able to be picked as your 'Art Scene' + `, + autoActivate: false, + autoView: false, + }, + }, + sheetFadeOpacityData: { + name: "Sheet Fade Opacity", + hint: "Change the opacity of the background when the sheet fades. The minimum is 20, nearly completely transparent, 100 means completely opaque.
You must refresh any open journals after changing this value to see the difference.", + value: 60, + }, + fadeSheetImagesData: { + name: "Fade Sheet Images", + hint: "When fading a JournalEntry, Actor, or Item sheet, should the images fade as well as the background?", + chosen: "fadeAll", + choices: { + fadeBackground: "Fade Background and UI Only", + fadeAll: "Fade Background, UI AND Images", + }, + }, + indicatorColorData: { + name: "Tile Indicator Colors", + hint: "Choose colors for the tile indicators, and the tile accent colors in the settings", + colors: { + frameTileColor: "#2ec4b6", + unlinkedTileColor: "#e71d36", + artTileColor: "#ff9f1c", + defaultTileColor: "#b260ff", + // frameTileColor: "#ed3000", + // artTileColor: "#009ec5", + // unlinkedTileColor: "#33ac4b", + // defaultTileColor: "#df00b2", + }, + propertyNames: { + frameTileColor: "--data-frame-color", + artTileColor: "--data-art-color", + unlinkedTileColor: "--data-unlinked-color", + defaultTileColor: "--data-default-color", + }, + }, + defaultTileImages: { + name: "Default Tile Images", + hint: "Choose images for the Art and Frame tiles when they're first created, and for art tiles to reset to when the tile is 'cleared'", + paths: { + frameTilePath: `${assetFolderBasePath}Bounding_Tile.webp`, + artTilePath: `${assetFolderBasePath}DarkBackground.webp`, + }, + }, +}; + +export const registerSettings = async function () { + await game.settings.registerMenu(MODULE_ID, "JTCSSettingsMenu", { + name: "JTCS Art Gallery Settings", + label: "Open JTCS Art Gallery Settings", + hint: "Configure extra Journal to Canvas Slideshow settings", + icon: "fas fa-bars", + type: JTCSSettingsApplication, + restricted: true, + }); + + await game.settings.register(MODULE_ID, "artGallerySettings", { + scope: "world", // "world" = sync to db, "client" = local storage + config: false, // we will use the menu above to edit this setting + type: Object, + default: artGalleryDefaultSettings, + onChange: async (event) => { + const updateData = await HelperFunctions.getSettingValue( + "artGallerySettings" + ); + Hooks.callAll("updateJTCSSettings", { origin: "JTCSSettings", updateData }); + }, + }); + + await game.settings.register( + "journal-to-canvas-slideshow", + "areConfigInstructionsVisible", + { + name: "Visible Art Gallery Tile Config Instructions", + hint: "Toggle whether the Art Gallery Configuration App will show instructions at the bottom of the application when you hover or not", + scope: "world", + config: true, + type: Boolean, + default: true, + } + ); + + game.settings.register("journal-to-canvas-slideshow", "showWelcomeMessage", { + name: "Show Welcome Message", + scope: "client", + type: Boolean, + config: true, + default: true, + }); +}; diff --git a/scripts/shim.js b/scripts/shim.js new file mode 100644 index 0000000..aaa7546 --- /dev/null +++ b/scripts/shim.js @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2021 fvtt-lib-wrapper Rui Pinheiro + +"use strict"; + +// A shim for the libWrapper library +export let libWrapper = undefined; + +export const VERSIONS = [1, 12, 1]; +export const TGT_SPLIT_RE = new RegExp("([^.[]+|\\[('([^'\\\\]|\\\\.)+?'|\"([^\"\\\\]|\\\\.)+?\")\\])", "g"); +export const TGT_CLEANUP_RE = new RegExp("(^\\['|'\\]$|^\\[\"|\"\\]$)", "g"); + +// Main shim code +Hooks.once("init", () => { + // Check if the real module is already loaded - if so, use it + if (globalThis.libWrapper && !(globalThis.libWrapper.is_fallback ?? true)) { + libWrapper = globalThis.libWrapper; + return; + } + + // Fallback implementation + libWrapper = class { + static get is_fallback() { + return true; + } + + static get WRAPPER() { + return "WRAPPER"; + } + static get MIXED() { + return "MIXED"; + } + static get OVERRIDE() { + return "OVERRIDE"; + } + + static register(package_id, target, fn, type = "MIXED", { chain = undefined, bind = [] } = {}) { + const is_setter = target.endsWith("#set"); + target = !is_setter ? target : target.slice(0, -4); + const split = target.match(TGT_SPLIT_RE).map((x) => x.replace(/\\(.)/g, "$1").replace(TGT_CLEANUP_RE, "")); + const root_nm = split.splice(0, 1)[0]; + + let obj, fn_name; + if (split.length == 0) { + obj = globalThis; + fn_name = root_nm; + } else { + const _eval = eval; + fn_name = split.pop(); + obj = split.reduce((x, y) => x[y], globalThis[root_nm] ?? _eval(root_nm)); + } + + let iObj = obj; + let descriptor = null; + while (iObj) { + descriptor = Object.getOwnPropertyDescriptor(iObj, fn_name); + if (descriptor) break; + iObj = Object.getPrototypeOf(iObj); + } + if (!descriptor || descriptor?.configurable === false) + throw new Error( + `libWrapper Shim: '${target}' does not exist, could not be found, or has a non-configurable descriptor.` + ); + + let original = null; + const wrapper = + chain ?? (type.toUpperCase?.() != "OVERRIDE" && type != 3) + ? function (...args) { + return fn.call(this, original.bind(this), ...bind, ...args); + } + : function (...args) { + return fn.call(this, ...bind, ...args); + }; + if (!is_setter) { + if (descriptor.value) { + original = descriptor.value; + descriptor.value = wrapper; + } else { + original = descriptor.get; + descriptor.get = wrapper; + } + } else { + if (!descriptor.set) throw new Error(`libWrapper Shim: '${target}' does not have a setter`); + original = descriptor.set; + descriptor.set = wrapper; + } + + descriptor.configurable = true; + Object.defineProperty(obj, fn_name, descriptor); + } + }; + + //************** USER CUSTOMIZABLE: + // Set up the ready hook that shows the "libWrapper not installed" warning dialog. Remove if undesired. + { + //************** USER CUSTOMIZABLE: + // Package ID & Package Title - by default attempts to auto-detect, but you might want to hardcode your package ID and title here to avoid potential auto-detect issues + const [PACKAGE_ID, PACKAGE_TITLE] = (() => { + const match = (import.meta?.url ?? Error().stack)?.match(/\/(worlds|systems|modules)\/(.+)(?=\/)/i); + if (match?.length !== 3) return [null, null]; + const dirs = match[2].split("/"); + if (match[1] === "worlds") + return dirs.find((n) => n && game.world.id === n) ? [game.world.id, game.world.title] : [null, null]; + if (match[1] === "systems") + return dirs.find((n) => n && game.system.id === n) + ? [game.system.id, game.system.data.title] + : [null, null]; + const id = dirs.find((n) => n && game.modules.has(n)); + return [id, game.modules.get(id)?.data?.title]; + })(); + + if (!PACKAGE_ID || !PACKAGE_TITLE) { + console.error( + "libWrapper Shim: Could not auto-detect package ID and/or title. The libWrapper fallback warning dialog will be disabled." + ); + return; + } + + Hooks.once("ready", () => { + //************** USER CUSTOMIZABLE: + // Title and message for the dialog shown when the real libWrapper is not installed. + const FALLBACK_MESSAGE_TITLE = PACKAGE_TITLE; + const FALLBACK_MESSAGE = ` +

'${PACKAGE_TITLE}' depends on the 'libWrapper' module, which is not present.

+

A fallback implementation will be used, which increases the chance of compatibility issues with other modules.

+

'libWrapper' is a library which provides package developers with a simple way to modify core Foundry VTT code, while reducing the likelihood of conflict with other packages.

+

You can install it from the "Add-on Modules" tab in the Foundry VTT Setup, from the Foundry VTT package repository, or from libWrapper's Github page.

+ `; + + // Settings key used for the "Don't remind me again" setting + const DONT_REMIND_AGAIN_KEY = "libwrapper-dont-remind-again"; + + // Dialog code + console.warn(`${PACKAGE_TITLE}: libWrapper not present, using fallback implementation.`); + game.settings.register(PACKAGE_ID, DONT_REMIND_AGAIN_KEY, { + name: "", + default: false, + type: Boolean, + scope: "world", + config: false, + }); + if (game.user.isGM && !game.settings.get(PACKAGE_ID, DONT_REMIND_AGAIN_KEY)) { + new Dialog({ + title: FALLBACK_MESSAGE_TITLE, + content: FALLBACK_MESSAGE, + buttons: { + ok: { icon: '', label: "Understood" }, + dont_remind: { + icon: '', + label: "Don't remind me again", + callback: () => game.settings.set(PACKAGE_ID, DONT_REMIND_AGAIN_KEY, true), + }, + }, + }).render(true); + } + }); + } +}); diff --git a/scripts/tests/test-utils.js b/scripts/tests/test-utils.js new file mode 100644 index 0000000..aa1306f --- /dev/null +++ b/scripts/tests/test-utils.js @@ -0,0 +1,302 @@ +"use strict"; +import { ArtTileManager } from "../classes/ArtTileManager.js"; +import { HelperFunctions } from "../classes/HelperFunctions.js"; +import { ImageDisplayManager } from "../classes/ImageDisplayManager.js"; + +export class TestUtils { + /** + * Dispatch a simulated event, for when the simple element.click() won't do + * @param {HTMLElement} element - the element we're toggling + * @param {*} eventInterface - the interface such as MouseEvent, ChangeEvent, KeyboardEvent + * @param {String} eventName - the name of the event, such as "mouseenter", "change", "keydown" + * @param {Object} options - object for extra options to pass to the event + * @param {Boolean} options.bubbles - whether the event bubbles + * @param {Boolean} options.ctrlKey - whether we should consider the ctrl key pressed or not + * @example + * + */ + static dispatchEvent( + element, + eventInterface, + eventName, + options = { + bubbles: true, + ctrlKey: false, + } + ) { + element.dispatchEvent(new eventInterface(eventName, options)); + } + static dispatchMouseEnter(element) { + element.dispatchEvent( + new MouseEvent("mouseenter", { + bubbles: true, + }) + ); + } + + /** + * Simulate keypress + * @param {*} element + */ + static dispatchKeypress(element, keyCode) { + element.dispatchEvent( + new KeyboardEvent("keydown", { key: keyCode, bubbles: true }) + ); + } + + /** + * Simulate clicks, especially those with Ctrl or other keys pressed + * @param {HTMLElement} element + */ + static dispatchMouseDown(element) { + element.dispatchEvent( + new MouseEvent("click", { + ctrlKey: true, // if you aren't going to use them. + bubbles: true, + metaKey: true, // these are here for example's sake. + }) + ); + } + /** + * Simulate a "change" event on an element + * @param {HTMLElement} element - the element we're simulatnig an event uppon + */ + static dispatchChange(element) { + // element.fireEvent("onchange"); + const e = new Event("change", { bubbles: true }); + element.dispatchEvent(e); + // element.dispatchEvent("onchange"); + } + static async getTileObject(tileID, sceneID = "") { + let tileObject = await ArtTileManager.getTileObjectByID(tileID, sceneID); + return tileObject; + } + static async getDocData(document, property = "") { + let data = game.version >= 10 ? document : document.data; + if (property) { + return foundry.utils.getProperty(data, property); + } else { + return data; + } + } + static async resizeTile(tileDoc, scene) { + //give it random dimensions bigger than scene + let width = (await TestUtils.getDocData(scene, "width")) + 30; + let height = (await TestUtils.getDocData(scene, "height")) + 30; + let updateData = { + _id: tileDoc.id, + width, + height, + }; + await scene.updateEmbeddedDocuments("Tile", [updateData]); + } + + /** + * change the tile's image to a test image + */ + static async changeTileImage(tileID, url = "") { + if (!url) + url = "/modules/journal-to-canvas-slideshow/demo-images/pd19-20049_1.webp"; + await ImageDisplayManager.updateTileObjectTexture(tileID, "", url, "anyScene"); + } + static async getArtGallerySettings(nestedPropertyString = "") { + let settings = await HelperFunctions.getSettingValue( + "artGallerySettings", + nestedPropertyString + ); + return settings; + } + + /** + * returns the "Computed Styles" Object or a property within if specified + * @param {JQuery or HTMLElement} element - the element whose styles we're getting + * @param {String} selector - the selector of the element + * @param {String} property - the style property we want to return + * @returns the Object representing the computed styles, or a property within + */ + static returnComputedStyles(element, selector, property = "") { + if (element.jquery) { + element = element[0]; + } + let selectedElement = element; + if (selector) selectedElement = element.querySelector(selector); + if (!property) return getComputedStyle(selectedElement); + else return getComputedStyle(selectedElement).getPropertyValue(property); + } + + static returnClassListAsArray(element, selector) { + if (element.jquery) { + element = element[0]; + } + return Array.from(element.querySelector(selector).classList); + } + static getDocIdFromApp(app) { + return app.document.id; + } + + /** + * We want to test each of the qualifications for if a frame tile + * is properly linked to an art tile here + * + * @param {Object} frameTile - the frame tile + * @param {Object} artTile - the art tile + */ + static async testFitToFrame(frameTileID, artTileID) { + const TU = TestUtils; + + let artTileDoc = await TU.getTileObject(artTileID); + let frameDoc = await TU.getTileObject(frameTileID); + //TODO - test if the art tile fits within the frame (tile or scene) + let areas = await TU.getAreasOfDocs(frameDoc, artTileDoc); + return areas; + + //TODO - test if the Config UI Updates to show that this art tile now has the frame tile as its child + } + + static async duplicateTestScene(sourceScene) { + // let dupedSceneData; + let dupedSceneData = sourceScene.clone({ name: "Test Scene" }); + if (game.version < 10) { + dupedSceneData = { + ...foundry.utils.duplicate(sourceScene), + _id: foundry.utils.randomID(), + name: "Test Scene", + }; + } + + let scene = await Scene.create(dupedSceneData); + await scene.activate(); + await scene.view(); + return scene; + } + static async initializeScene(name = "Display") { + let sourceScene = game.scenes.getName(name); + await sourceScene.view(); + return sourceScene; + } + + /** + * Get the auto-view or auto-activate settings of the dedicatedDisplayData property + * @param {String} sceneOrJournal - whether we want to access the scene or the journal sub-object + * @param {String} viewOrActivate - whether we want to get the view or activate property + */ + static async getAutoViewOrActivate( + sceneOrJournal = "scene", + viewOrActivate = "view" + ) { + const key = `dedicatedDisplayData[${sceneOrJournal}].auto${viewOrActivate}`; + const current = await HelperFunctions.getSettingValue(`artGallerySettings`, key); + return { key, current }; + } + /** + * Toggle the auto-view or auto-activate settings of the dedicatedDisplayData property + * @param {String} sceneOrJournal - whether we want to access the scene or the journal sub-object + * @param {String} viewOrActivate - whether we want to toggle the view or activate property + */ + static async toggleAutoViewOrActivate(...args) { + let { key, current } = await TestUtils.getAutoViewOrActivate(...args); //(sceneOrJournal, viewOrActivate) + // const key = `dedicatedDisplayData[${sceneOrJournal}].auto${viewOrActivate}`; + // const current = await HelperFunctions.getSettingValue(`artGallerySettings`, key); + await HelperFunctions.setSettingValue("artGallerySettings", key, !current); + } + + static async clickGlobalButton(configElement, actionName) { + configElement[0] + .querySelector(`[data-action='globalActions.click.actions.${actionName}']`) + .click(); + await quench.utils.pause(900); + } + + /** + * @description - renders the Scene Config + */ + static async renderConfig() { + let configApp = new SlideshowConfig(); + await configApp._render(true); + let configElement = configApp.element; + return { configApp, configElement }; + } + + static async getDefaultImageSrc(type = "art") { + let defaultImageSrc = await TestUtils.getArtGallerySettings( + `defaultTileImages.paths.${type}TilePath` + ); + return defaultImageSrc; + } + + static async clickActionButton(actionName, element, options = { quench }) { + const actionQueryString = combine(actionName); + await clickButton(element, actionQueryString, quench); + } + + /** + * click a button, optionally one with a data-action attribute + * @param {HTMLElement} element - the element to click + * @param {Selector} selector - the selector of the button we want to find + * @param {Object} options - options object + * @param {Object} options.quench - the quench thing to pause the button + * @param {String} options.actionName - the individual action name + * @param {String} options.parentPath - the parent path to find the options' name + */ + static async clickButton(element, selector, options) { + const { quench, actionName, parentPath } = options; + let ourButton = element.querySelector(selector); + ourButton.click(); + await quench.utils.pause(900); + } + static getChildElements(element, selector, multiple = false) { + if (element.jquery) { + element = element[0]; + } + if (multiple) { + return Array.from(element.querySelectorAll(selector)); + } else { + return element.querySelector(selector); + } + } + static getAppFromWindow(type, searchText = "") { + let windows = Object.values(ui.windows); + function predicate(w) { + let allTrue = false; + if (w instanceof type) { + if (!searchText) { + allTrue = true; + } else { + if (w.element && w.element.text().includes(searchText)) + allTrue = true; + } + return allTrue; + } + } + return Object.values(ui.windows).filter(predicate)[0]; + } + static checkAppElementForId(app, id) { + let elementId = app.element?.attr("id"); + if (!app.element || elementId != id) { + return false; + } else { + return true; + } + } + + static async deleteTestScene(scene) { + let dupedSceneId = scene.id; + await Scene.deleteDocuments([dupedSceneId]); + } + static async getDimensions(doc) { + let width = await TestUtils.getDocData(doc, "width"); + let height = await TestUtils.getDocData(doc, "height"); + return { + width, + height, + }; + } + static async getAreasOfDocs(frameDoc, artTileDoc) { + const frameDimensions = await TestUtils.getDimensions(frameDoc); + const frameArea = frameDimensions.width * frameDimensions.height; + + const artDimensions = await TestUtils.getDimensions(artTileDoc); + const artArea = artDimensions.width * artDimensions.height; + return { artArea, frameArea }; + } +} diff --git a/styles/_animations.scss b/styles/_animations.scss new file mode 100644 index 0000000..cbf16dd --- /dev/null +++ b/styles/_animations.scss @@ -0,0 +1,17 @@ +@keyframes fade { + from { + opacity: 0%; + } + + to { + opacity: 100%; + } +} +@keyframes fadeOut { + from { + opacity: 100%; + } + to { + opacity: 0%; + } +} diff --git a/styles/_colors.scss b/styles/_colors.scss new file mode 100644 index 0000000..b0abddf --- /dev/null +++ b/styles/_colors.scss @@ -0,0 +1,64 @@ +$colors: ( + secondary: ( + darkest: hsl(221, 98%, 20%), + 800: hsl(215, 98%, 30%), + 700: hsl(210, 98%, 40%), + 600: hsl(205, 98%, 52%), + base: hsl(199, 98%, 63%), + 400: hsl(190, 99%, 70%), + 300: hsl(185, 100%, 80%), + lightest: hsl(175, 100%, 90%), + ), + primary: ( + darkest: hsl(240, 58%, 28%), + 800: hsl(240, 58%, 30%), + 700: hsl(240, 58%, 40%), + 600: hsl(240, 60%, 50%), + base: hsl(240, 60%, 60%), + 400: hsl(240, 60%, 68%), + 300: hsl(240, 65%, 75%), + 200: hsl(240, 75%, 85%), + lightest: hsl(240, 86%, 92%), + ), + deep-purple: ( + darkest: hsl(269, 100%, 8%), + 700: hsl(269, 100%, 18%), + 600: hsl(269, 100%, 28%), + base: hsl(269, 100%, 38%), + ), + neutral: ( + darkest: hsl(240, 17%, 20%), + 700: hsl(240, 17%, 30%), + base: hsl(240, 12%, 50%), + 300: hsl(240, 17%, 70%), + lightest: hsl(240, 17%, 90%), + ), + glass: ( + no-blur: hsla(0, 0%, 44%, 0.478), + blur: hsla(0, 0%, 44%, 0.267), + border: hsla(0, 0%, 100%, 0.198), + no-blur-dark: hsla(240, 11%, 14%, 0.769), + blur-dark: hsla(240, 11%, 14%, 0.161), + border-dark: hsla(240, 10%, 40%, 0.568), + ), + danger: ( + dark: hsl(0, 89%, 20%), + base: hsl(0, 59%, 50%), + light: hsl(0, 99%, 70%), + ), + warning: ( + dark: hsl(28, 100%, 30%), + base: hsl(23, 80%, 50%), + light: hsl(21, 100%, 65%), + ), + success: ( + dark: hsl(100, 100%, 30%), + base: hsl(100, 100%, 50%), + light: hsl(100, 100%, 70%), + ), + info: ( + dark: hsl(200, 100%, 25%), + base: hsl(200, 60%, 40%), + light: hsl(200, 80%, 60%), + ), +); diff --git a/styles/_mixins.scss b/styles/_mixins.scss new file mode 100644 index 0000000..0596043 --- /dev/null +++ b/styles/_mixins.scss @@ -0,0 +1,229 @@ +@mixin with-absolute-pseudo($centeredX: true, $centeredY: true, $isBefore: true) { + $pseudoSelector: before; + @if ($isBefore != true) { + $pseudoSelector: after; + } + position: relative; + &:#{$pseudoSelector} { + position: absolute; + content: ""; + @if ($centeredX and not $centeredY) { + left: 50%; + transform: translateX(-50%); + } @else if ($centeredY and not $centeredX) { + top: 50%; + transform: translateY(-50%); + } @else if($centeredX and $centeredY) { + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + } +} +.blank-button { + border: none; + background: transparent; + margin: unset; + padding: unset; +} + +@function returnAllHeadings($startLevel: 1, $endLevel: 6) { + $selectorList: []; + @for $var from $startLevel to $endLevel { + $headerSelector: "h#{$var}"; + $selectorList: join($selectorList, $headerSelector); + } + @return $selectorList; +} +@mixin printSelectors($selectorList, $propertyMap) { + @for $i from 1 to length($selectorList) { + #{nth($selectorList, $i)} { + @each $property, $value in $propertyMap { + #{$property}: $value; + } + } + } +} +@mixin text-shadow($shadow-color: black) { + text-shadow: -1px -1px 0 $shadow-color, 0 -1px 0 $shadow-color, + 1px -1px 0 $shadow-color, 1px 0 0 $shadow-color, 1px 1px 0 $shadow-color, + 0 1px 0 $shadow-color, -1px 1px 0 $shadow-color, -1px 0 0 $shadow-color; +} + +@mixin JTCS-accent-ghost($primary-color: var(--JTCS-accent-color), $border-side: "") { + background-color: transparent; + color: $primary-color; + border#{$border-side}: 2px solid $primary-color; + transition: background-color 120ms ease-in, color 120ms ease-in; + &:hover, + &:focus, + &.active { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: $primary-color; + // color: var(--JTCS-text-color-on-bg); + color: var(--JTCS-text-color-on-fill); + } +} +@mixin JTCS-accent-ghost-input-span($primary-color: var(--JTCS-accent-color)) { + input:disabled + label { + opacity: 50%; + } + display: inline-flex; + &[data-variant="visible-tick"] { + input[type="radio"], + input[type="checkbox"] { + @extend .JTCS-hidden; + + + label { + background-color: transparent; + color: $primary-color; + border: 2px solid $primary-color; + + transition: background-color 120ms ease-in, color 120ms ease-in; + &:hover, + &:focus { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: $primary-color; + color: var(--JTCS-text-color-on-fill); + } + .tick-circle, + .tick-box { + border: 1px solid $primary-color; + display: inline-flex; + width: 1rem; + height: 1rem; + background-color: var(--JTCS-background-color); + } + .tick-circle { + border-radius: 50%; + } + } + &:checked { + + label { + color: var(--JTCS-text-color-on-fill); + background-color: $primary-color; + border: 2px solid $primary-color; + outline: 1.5px solid transparent; + outline-offset: -1px; + // transition: color 200ms ease-out, background-color 200ms ease-out, border-color 200ms ease-out; + transition: outline-color 200ms ease-out, box-shadow 200ms ease-out; + &:hover { + outline-color: white; + box-shadow: 0px 0px 4px 2px $primary-color; + color: var(--JTCS-text-color-on-fill); + // color: var(--JTCS-text-color-on-bg); + } + // background-color: var(--accent-color); + .tick-circle, + .tick-box { + @include with-absolute-pseudo($isBefore: false); + &:after { + width: 0.5rem; + height: 0.5rem; + } + } + .tick-circle { + &:after { + border-radius: 50%; + background-color: $primary-color; + } + } + .tick-box { + &:after { + content: "✓"; + color: $primary-color; + top: unset; + } + } + } + } + } + } +} + +@mixin JTCS-accent-fill( + $primary-color: var(--JTCS-accent-color), + $include-shadow: false, + $hoverStyle: "scale", + $is-icon-button: false +) { + background-color: $primary-color; + @if $include-shadow { + box-shadow: 0px 0px 8px 2px $primary-color; + } @else { + box-shadow: unset; + } + color: var(--JTCS-text-color-on-fill); + border: unset; + @if $hoverStyle == "scale" { + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; + } @else if $hoverStyle == "slide-in" { + position: relative; + overflow: hidden; + i { + display: inline-flex; + align-items: center; + justify-content: center; + position: absolute; + right: 100%; + transform: translateX(0); + transition: transform 220ms ease-in; + background-color: var(--JTCS-accent-color-20); + height: 100%; + width: 30%; + } + } @else if $hoverStyle == "border" { + position: relative; + border: 2px solid transparent; + transition: border-color 150ms ease-in; + // &:after { + // position: absolute; + // content: ""; + // top: 50%; + // left: 50%; + // transform: translate(-50%, -50%); + // width: 100%; + // height: 100%; + // z-index: 1; + // border-radius: 3px; + // border: 2px solid var(--JTCS-accent-color-20); + // opacity: 0; + // transition: opacity 240ms ease-in; + // } + } + &:hover, + &:focus { + @if $hoverStyle == "scale" { + transform: scale(1.12); + } @else if $hoverStyle == "slide-in" { + i { + transform: translateX(100%); + transition: transform 220ms ease-out; + } + } @else if $hoverStyle == "border" { + border: 2px solid var(--JTCS-accent-color-80); + transition: border-color 150ms ease-out; + // &:after { + // opacity: 100%; + // transition: opacity 240ms ease-out; + // } + } + } +} +@mixin generateTypes( + $list: ( + "frame", + "art", + "unlinked", + "default", + ), + $propertyPrefix: "" +) { + @each $var in $list { + .#{$var}-color { + #{$propertyPrefix}color: var(--data-#{$var}-color); + } + } +} diff --git a/styles/_spacing.scss b/styles/_spacing.scss new file mode 100644 index 0000000..a3147b1 --- /dev/null +++ b/styles/_spacing.scss @@ -0,0 +1,5 @@ +$spacing: ( + "small": 1rem, + "medium": 2rem, + "large": 3rem, +); diff --git a/styles/_utility.scss b/styles/_utility.scss new file mode 100644 index 0000000..3c93927 --- /dev/null +++ b/styles/_utility.scss @@ -0,0 +1,1252 @@ +@use "variables" as *; +@use "mixins" as *; +@use "animations" as *; +@use "sass:string"; + +:root { + //foundry colors + + // --foundry-default-background-texture: url("/ui/parchment.jpg") repeat; + --bg-color-hover: var(--color-border-dark-primary); + --bg-color: hsla(0, 0%, 20%, 0.9); + --bg-color-toggle: var(--foundry-purple-background); + --bg-color-toggle-hover: var(--foundry-purple-background-hover); + --border-color-toggle: var(--foundry-purple-border); + --shadow-color-toggle-hover: var(--foundry-purple-shadow-hover); + --color-text-primary: var(--color-text-dark-primary); + --color-text-secondary: var(--color-text-light-0); + --color-text-secondary-hover: var(--color-text-light-highlight); + --shadow-color: var(--color-shadow-dark); + --shadow-color-hover: var(--color-shadow-highlight); + --border-color: var(--color-border-dark); + --border-color-hover: var(--color-border-highlight); + --border-color-hover-alt: var(--color-border-highlight-alt); + --accent-color-1: var(--foundry-purple-shadow-hover, #9b8dff); + --accent-color-2: var(--color-border-highlight); + --color-accent: var(--foundry-purple-background); +} + +.popover { + // animation: fade 0.3s linear; + position: absolute; + border-top: 4px solid var(--JTCS-accent-color); + + padding: var(--padding-small); + background-color: var(--JTCS-elevation-BG-color); + color: var(--JTCS-text-color-on-bg); + top: 100%; + min-width: max-content; + box-shadow: var(--box-shadow); + select { + color: var(--color-text-light-0); + } + input[type="text"] { + color: var(--color-text-dark-primary, black) !important; + } + &[data-variant="over"] { + bottom: 100%; + } +} + +.fill-parent { + width: 100%; + height: 100%; +} + +.reset-margin { + margin: 0; +} +.reset-padding { + padding: 0; +} +.full-parent-height { + height: 100%; +} + +.flow > * + * { + margin-top: var(--flow-space, 1em); +} + +.img-button { + background: none; + background-color: transparent; + width: fit-content; + height: fit-content; + img { + max-width: 3rem; + // filter: inherit; + // filter: drop-shadow(2px 4px 6px black); + // transition: filter 150ms ease-in; + } + &.framed { + position: relative; + img { + clip-path: circle(50%); + } + &:before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + background-image: url("/modules/hud-and-trackers/images/Button Badges/Frames & Borders/circle-frame.webp"); + } + } +} +.danger { + background-color: var(--JTCS-danger-color); + color: var(--JTCS-text-color-on-fill); +} +.danger-text { + color: var(--JTCS-danger-color); +} + +.toggle, +.toggle.active { + --bg-color: var(--bg-color-toggle); + --shadow-color-hover: var(--shadow-color-toggle-hover); + --border-color-hover: var(--border-color-toggle); + &:focus { + --color-shadow-primary: #3a00ae; + } +} + +.button-container { + position: relative; + list-style-type: none; + .nested-buttons { + display: flex; + position: absolute; + top: 0; + left: 100%; + button { + white-space: nowrap; + } + } +} + +.floating-control { + color: var(--color-text-secondary); + background-color: var(--bg-color); + box-shadow: 0 0 10px var(--shadow-color); + border: 1px solid var(--border-color); + i { + margin: 0; + } + &:hover, + &.active { + cursor: pointer; + box-shadow: 0 0 10px var(--shadow-color-hover); + border: 1px solid var(--border-color-hover); + color: var(--color-text-secondary-hover); + } +} +.toggle-visible { + // opacity: 100%; + // transition: opacity 500ms ease-in; +} +.fade-to-hidden { + opacity: 0%; + transition: opacity 500ms ease-out; +} +.JTCS-hidden { + // opacity: 0%; + // transition: opacity 500ms ease-out; + transform: scale(0); + position: absolute; + overflow: hidden; + clip: rect(0 0 0 0); + height: 1px; + width: 1px; + margin: -1px; + padding: 0; + border: 0; +} + +input:is([type="radio"], [type="checkbox"]).hidden-input { + @extend .JTCS-hidden; + + & + label { + transform: scale(1); + color: var(--color-text-secondary); + background-color: var(--bg-color); + box-shadow: 0 0 10px transparent; + border: 1px solid var(--border-color); + padding: 0.5rem 0.5rem; + overflow: visible; + width: fit-content; + width: max-content; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 150ms ease-out transform 150ms ease-out; + &:hover { + cursor: pointer; + border: 1px solid var(--border-color-hover); + color: var(--color-text-secondary-hover); + box-shadow: 0 0 10px var(--shadow-color-hover); + } + } + &:checked + label { + transform: scale(1.04); + z-index: 1; + // --bg-color-hover: var(--color-deep-purple-600); + color: var(--color-text-secondary-hover); + box-shadow: 0px 0px 10px var(--shadow-color-hover); + border: 1px solid var(--color-border-highlight-alt); + transition: background-color 250ms ease-out; + &:hover { + color: var(--shadow-color-hover); + } + .tooltip { + box-shadow: 0px 0px 10px var(--shadow-color-hover); + border: 1px solid var(--color-border-highlight-alt); + } + // &:not(:hover) { + // // background-color: var(--color-deep-purple-700); + // } + } +} + +.minimize { + transform: scaleY(0); + opacity: 0; + height: fit-content; + pointer-events: auto; + display: flex; + flex-direction: column; + transform-origin: top center; + transition: transform 100ms ease-out, opacity 100ms ease-out; +} + +.input-span { + @include JTCS-accent-ghost-input-span(); +} +p.inline-notification { + display: inline-flex; + $variants: (info, warning, danger, success); + @each $type in $variants { + &[data-variant="#{$type}"] { + // color: var(--color-#{$type}-dark); + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-#{$type}-color); //var(--color-#{$type}-light); + .icon-span i { + color: var(--color-#{$type}-dark); + margin-right: 0.25rem; + } + } + } +} + +input[type="radio"]:checked.accent-input { + --accent-color: var(--accent-color-1); + accent-color: var(--accent-color); + filter: unset; + -webkit-filter: unset; +} + +.fit-content-width { + width: fit-content; +} +.hover-reveal { + position: relative; + & > &--child { + opacity: 0; + transition: opacity 250ms ease-in; + } + &:hover { + .hover-reveal--child { + opacity: 100%; + } + } +} +.hover-reveal-right { + position: relative; + &:hover { + .hover-reveal-right-child { + opacity: 100%; + } + } + &-child { + position: absolute; + opacity: 0; + left: 100%; + top: 0; + } +} +.hover-reveal-up { + position: relative; + &:hover, + &.open { + .hover-reveal-up-child { + opacity: 1; + // transform: scale(1); + } + } + &-child { + position: absolute; + bottom: 100%; + left: 0; + transform-origin: bottom center; + // transform: scale(0); + opacity: 0; + transition: opacity 150ms ease-out backdrop-filter 150ms ease-out; + // transition: transform 150ms ease-out; + } +} +fieldset.toggle-button-group { + max-width: fit-content; + box-shadow: var(--box-shadow); + pointer-events: auto; + * { + pointer-events: auto; + } + legend { + background-color: inherit; + padding: 0.5rem; + } + display: flex; + flex-direction: row; + border: none; + color: currentColor; + padding: 0rem 0rem; + min-width: 50%; + label:first-of-type { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + label:last-of-type { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + } +} +.full-width { + width: 100%; +} + +.interactive { + cursor: pointer; +} +.fade { + background: rgba(255, 255, 255, var(--journal-fade, 50%)) !important; + &[data-fade-all] { + opacity: var(--journal-fade, 50%); + .editor-content { + img { + opacity: var(--journal-fade, 50%) !important; + } + } + } +} +#slideshow-config { + .fade { + background: var(--JTCS-background-color) !important; + } +} +$sizes: ( + "small": 0.5rem, + "medium": 1rem, + "large": 1.5rem, +); +$directions: ("top", "left", "bottom", "right", "inline", "block"); +@each $size, $value in $sizes { + @each $direction in $directions { + .padding-#{$direction}-#{$size} { + padding-#{$direction}: $value; + } + } +} + +.padding-small { + padding: 0.25rem 0.5rem; +} +.padding-medium { + padding: 0.5rem 1rem; +} +.padding-large { + padding: 1rem 1.5rem; +} +.padding-none { + padding: 0; +} +.margin-none { + margin: 0; +} +button { + cursor: pointer; +} +button.icon-button { + height: 0; + padding: 0; + margin: 0; + border: none; + background: none; + background-color: transparent; + width: 35%; + padding-bottom: 35%; + position: relative; + i { + pointer-events: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + } + &[data-variant="small"] { + width: 5%; + padding-bottom: 5%; + } + &.active { + @include JTCS-accent-fill($include-shadow: true, $is-icon-button: true); + } +} +$text-align-directions: ("left", "center", "right"); + +@each $direction in $text-align-directions { + .text-align-#{$direction} { + text-align: #{$direction}; + } +} + +.hover-text-color { + &:hover { + color: var(--color-accent); + } +} +.bg-fill { + background-color: var(--bg-color); + color: var(--text-color); +} +.bg-ghost { + background-color: transparent; + border: 2px solid var(--border-color); + color: var(--text-color); +} + +.bg-dark { + background-color: var(--bg-color); + color: var(--color-text-secondary); +} + +.bg-primary { + background-color: var(--bg-color); +} + +.bg-hover { + background-color: var(--bg-color-hover); +} +.bg-hover-toggle { + background-color: var(--bg-color-toggle-hover); +} + +.bg-none { + background: none; + background-color: transparent; +} + +.bg-neutral-base { + background-color: var(--color-neutral-base); +} +.bg-neutral-darkest { + background-color: var(--color-neutral-darkest); +} +.bg-neutral-lightest { + background-color: var(--color-neutral-lightest); +} +.bg-glass { + transition: background-color 100ms ease-in backdrop-filter 100ms ease-in; + background-color: var(--color-glass-no-blur); + @supports (backdrop-filter: blur(12px)) { + background-color: var(--color-glass-blur); + backdrop-filter: blur(12px) opacity(1); + } + border: 1px solid var(--color-glass-border); +} + +.hover-grow { + transition: transform 150ms ease-in; + &:hover { + transform: scale(1.2); + } +} +.hover-bg-primary { + transition: background-color 150ms ease-in; + &:hover { + background-color: var(--color-secondary-base); + } +} +.hover-bg-secondary { + transition: background-color 150ms ease-in; + &:hover { + background-color: var(--color-secondary-base); + } +} +.hover-bg-removable { + transition: background-color 150ms ease-in; + &:hover { + background-color: var(--JTCS-danger-color); + } +} +.hover-bg-none { + &:hover { + background: none; + background-color: transparent; + } +} +.hover-bg-dynamic { + transition: background-color 150ms ease-in; + &:hover { + background-color: var(--bg-color); + } +} +.hover-border { + border: 2px solid transparent; + &[data-variant="bottom"] { + border: unset; + transition: border-color 150ms ease-in; + border-bottom: 2px solid var(--JTCS-background-color-10); + } + transition: border-color 150ms ease-in; + &:hover { + border-color: var(--accent-color); + &[data-variant="bottom"] { + border: unset; + border-bottom: 2px solid; + } + transition: border-color 150ms ease-out; + } +} +.border-bottom { + border: unset; + border-bottom: 2px solid var(--JTCS-background-color-20); +} +.focus-border { + outline: 3px solid transparent; + transition: outline-color 150ms ease-in; + &:focus, + &:focus-within { + outline-color: var(--accent-color); + transition: outline-color 150ms ease-out; + } +} + +.focus-bg-none { + &:focus { + background: none; + background-color: transparent; + } +} + +.hover-disabled { + &:disabled { + cursor: not-allowed; + } +} +.brighten-on-open { + &.open { + filter: brightness(1.2); + } +} +.glow-on-open { + &.open { + filter: drop-shadow(0 0 0.75rem crimson); + } +} +.child-glow-on-open { + &.open { + > img { + filter: drop-shadow(0 0 0.75rem crimson); + } + } +} +.drop-shadow { + filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.35)); +} + +.max-height-full-parent { + max-height: 100%; +} + +.text-shadow { + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +.shadow-none { + box-shadow: var(--shadow-none); +} + +.shadow-lowest { + box-shadow: var(--shadow-lowest); +} +.shadow-base { + box-shadow: var(--shadow-base); +} + +.shadow-highest { + box-shadow: var(--shadow-highest); +} +.shadow-circle { + filter: drop-shadow(2px 4px 6px black); + transition: filter 150ms ease-in; +} + +.shadow-inset { + box-shadow: var(--box-shadow-inset); +} +.overflow { + $types: (auto, scroll, hidden); + @each $type in $types { + &-#{$type} { + overflow: $type; + &-x { + overflow-x: $type; + } + &-y { + overflow-y: $type; + } + } + } +} +.overflow-auto-y { + overflow-y: auto; +} + +.scroll-y { + overflow-y: scroll; +} + +.scroll-x { + overflow-x: scroll; +} + +.hidden-overflow-y { + overflow-y: hidden; +} +.hidden-overflow-x { + overflow-x: hidden; +} + +.border-color-shadow { + --border-color: rgba(0, 0, 0, 0.25); +} + +.border-block { + border-block: 2px solid var(--border-color); +} +.border-inline { + border-inline: 2px solid var(--border-color); +} +.border-full { + border: 2px solid var(--border-color); +} + +.square { + width: 2rem; + height: 2rem; +} +.square-medium { + width: 4rem; + height: 4rem; +} +.square-large { + width: 6rem; + height: 6rem; +} + +.before-color-overlay { + &:before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + background-color: var(--bg-color); + mix-blend-mode: color; + opacity: 20%; + transition: opacity 100ms ease-in; + } +} +.before-hover-opacity { + &:hover { + &:before { + opacity: 100%; + } + } +} +.rounded-outer-children { + &:first-child button { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + &:last-child button { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + } +} + +.horizontal-button-wrapper { + display: flex; + align-items: center; + justify-content: space-evenly; +} + +.button-list { + display: flex; + + li { + list-style-type: none; + } +} +.sub-section { + background-color: var(--bg-color); + border-radius: 10px; +} + +[data-title] { + position: relative; + .tooltip { + font-size: var(--font-size-12); + transition-delay: 0ms; + transition: opacity 170ms ease-out; + pointer-events: none; + opacity: 0%; + position: absolute; + top: 100%; + left: 0; + + min-width: calc(100% + 10ch); + max-width: 210%; + &.tooltip-right { + left: unset; + top: 100%; + right: 100%; + } + &.tooltip-wide { + width: fit-content; + max-width: unset; + } + span { + pointer-events: none; + white-space: nowrap; + } + span:first-of-type { + color: var(--accent-color-1); + } + span:last-of-type { + color: var(--accent-color-2); + } + + // max-width: 200%; + } + &:hover { + .tooltip { + opacity: 100%; + + transition: opacity 320ms ease-out; + transition-delay: 500ms; + } + } +} + +.header { + display: flex; + flex-direction: column; + justify-content: center; + gap: 1rem; +} + +.config-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1rem; +} + +.drawer-visible { + right: unset; + left: 0px; +} + +.side-drawer { + position: absolute; + right: 100%; + z-index: 200; + width: 50%; + + .tab { + position: absolute; + left: 100%; + top: 50%; + width: 3rem; + height: 3rem; + background-color: var(--color-secondary-base); + color: white; + z-index: 200; + } +} + +.justify-self-right { + margin-left: auto; +} +.justify-self-left { + margin-right: auto; +} +.justify-self-center { + @extend .justify-self-left; + @extend .justify-self-right; +} + +.has-floating-label { + box-sizing: border-box; + position: relative; + --form-field-height: fit-content; + .floating-label { + color: var(--foundry-dark-gray); + position: absolute; + top: 0.1em; + left: 0.5em; + background: inherit; + font-size: smaller; + } + input, + select { + padding-top: 1.25em; + padding-bottom: 0.25em; + } +} + +.flex-row { + display: flex; + flex-direction: row; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex { + &-wrap { + flex-wrap: wrap; + } + &-nowrap { + flex-wrap: nowrap; + } +} + +.flex-centered { + display: flex; + justify-content: center; + align-items: center; +} +.flex-justify { + &-center { + justify-content: center; + } + &-stretch { + justify-content: stretch; + } + + &-space-between { + justify-content: space-between; + } + &-space-evenly { + justify-content: space-evenly; + } + &-start { + justify-content: flex-start; + } + &-end { + justify-content: flex-end; + } +} +.flex-align { + &-center { + align-items: center; + } + &-start { + align-items: flex-start; + } +} +.gap-small { + gap: 0.25rem; +} + +.gap-medium { + gap: 0.5rem; +} +.gap-large { + gap: 1rem; +} +// .hover-reveal { +// > &--child { +// opacity: 0; +// // transform: scale(0); +// } +// &:hover { +// > .hover-reveal--child { +// opacity: 100%; +// // transform: scale(1); +// } +// } +// } + +.has-absolute-child { + position: relative; + .absolute--child { + position: absolute; + } +} + +.focus-reveal { + & > &--child { + opacity: 0; + &[data-variant="scale"] { + transform: scale(0); + transition: transform scale 150ms; + } + } + &:focus, + &:focus-within { + > .focus-reveal--child { + opacity: 100%; + &[data-variant="scale"] { + transform: scale(1); + } + } + } +} + +.chip-span { + display: inline-flex; + justify-content: space-evenly; + height: fit-content; + width: 100%; + gap: 0.5rem; + padding: 0.5rem; + label { + border-radius: 999px; + min-width: 13ch; + } +} + +.wrap { + flex-wrap: wrap; +} + +//text +.text-centered { + text-align: center; +} + +//wether filled or ghost +//what color the background is +//what color the border is +//what color the text is + +.ghost { + background-color: transparent; + color: var(--color-primary-base); + border: 1px solid white; + border-color: var(--color-primary-base); + + &:hover { + border-color: var(--color-secondary-base); + color: var(--color-secondary-base); + } +} + +.filled { + background-color: var(--color-primary-base); + border-color: var(--color-primary-base); + color: white; + transition: border-color 100ms ease-in, background-color 100ms ease-in; + + &:hover { + background-color: var(--color-secondary-base); + border-color: var(--color-secondary-base); + } +} + +//for things like removing tags with a click +.remove-on-click { + position: relative; + + &:before { + position: absolute; + pointer-events: none; + width: 100%; + height: 100%; + border-radius: inherit; + background-color: rgb(255, 92, 63); + content: "Remove?"; + opacity: 0; + transition: opacity 100ms ease-in; + } + + &:hover { + &:before { + opacity: 100%; + } + } +} + +.bg-accent { + background-color: var(--accent-color); + // border-color: var(--color-primary-base); +} + +.sharp-corners { + border-radius: 0px; +} + +.rounded { + border-radius: 8px; +} + +.slight-rounded { + border-radius: 5px; +} + +.cap-rounded { + border-radius: 999px; +} +.single-sharp-corner { + border-top-left-radius: 0; +} + +// #region Colors + +.text-white { + color: white; +} + +.text-warning { + color: rgb(255, 92, 63); +} + +.text-danger { + color: rgb(255, 92, 63); +} + +.text-primary { + color: var(--color-primary-base); +} + +.text-accent { + color: var(--color-accent); +} +.text-heavy { + font-weight: bold; +} +.border-styled { + border: 2px solid var(--border-color); + &[data-variant="bottom"] { + border: unset; + border-bottom: 2px solid var(--border-color); + } +} + +.border-primary { + border-color: var(--color-primary-base); +} + +.border-accent { + border-color: var(--color-accent); +} + +.max-height-50 { + max-height: 50%; +} + +// #endregion + +.hover-shadow { + &:before { + pointer-events: none; + position: absolute; + width: 100%; + height: 100%; + content: ""; + border-radius: inherit; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.25); + opacity: 0%; + transition: opacity 100ms ease-out; + } + + &:hover { + &:before { + opacity: 100%; + } + } +} + +.window-padding { + padding: clamp(0.5rem, 0.5rem + 1vw, 1rem) clamp(0.5rem, 0.5rem + 1vh, 1rem); +} + +.window-gap { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.input-and-label-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.file-input-wrapper { + display: flex; + max-width: 100%; + + button { + //grow none, shrink normal scale + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: space-evenly; + height: 100%; + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } + + input { + //max, min, ideal + //grow normal scale, shrink none + flex: 1 0 auto; + max-width: 60%; + height: 100%; + border-top-right-radius: 0; + border-bottom-right-radius: 0; + } +} +.capitalize { + text-transform: capitalize; +} +.item-menu { + @extend .overflow-hidden; + display: flex; + justify-content: space-between; + flex-direction: column; + hr.list-divider { + width: 100%; + border-bottom: unset; + border-top: unset; + border: 1px solid var(--JTCS-accent-color); + @extend .reset-margin; + margin-top: unset; + margin-bottom: unset; + } + li { + color: var(--JTCS-text-color-on-bg); + background-color: var(--JTCS-elevation-BG-color); + &:first-child { + margin-top: 1em; + // padding-top: 2em; + } + &:last-child { + padding-top: 1em; + padding-bottom: 1em; + } + width: 100%; + --bg-color: var(--JTCS-accent-color); + --text-color: var(--JTCS-text-color-on-fill); + @extend .hover-bg-dynamic; + &:hover { + color: var(--JTCS-text-color-on-fill); + } + i { + display: grid; + place-content: center; + margin-right: 1.5rem; + } + } +} + +#JTCS-custom-dialog { + height: fit-content; + .window-content { + h1 { + font-size: 1.5rem; + border-bottom: unset; + } + input { + width: 100%; + } + } + .dialog-content { + display: flex; + flex-direction: column; + align-items: center; + @extend .padding-large; + color: var(--JTCS-text-color-on-bg); + } + .dialog-buttons { + flex: 0; + display: flex; + gap: 0.5rem; + } + &.dialog-grid { + .dialog-buttons { + display: grid; + grid-template-columns: repeat(4, 1fr); + } + } + &.dialog-grid-3 { + .dialog-buttons { + display: grid; + grid-template-columns: repeat(3, 1fr); + } + } + .dialog-buttons .dialog-button { + [data-button="delete"], + [data-button="reset"] { + background: var(--JTCS-danger-color); + color: white; + font-weight: bold; + } + } + .dialog-buttons .dialog-button { + @include JTCS-accent-ghost; + @extend .blank-button; + &[data-button="delete"], + &[data-button="reset"] { + @include JTCS-accent-fill($primary-color: var(--JTCS-danger-color)); + // background: var(--JTCS-danger-color); + color: var(--JTCS-text-color-on-fill); + font-weight: bold; + } + $buttonTypes: ( + delete: var(--JTCS-danger-color), + reset: var(--JTCS-danger-color), + cancel: transparent, + "window": pink, + journalEntry: rgb(94, 169, 226), + artScene: cyan, + ); + @each $type, $color in list { + &[data-button="#{$type}"] { + background: $color !important; + } + } + } + .destructive-action-prompt { + @extend .flex-centered; + display: flex; + flex-direction: column; + flex: 1 0 0; + h1 { + display: inline-flex; + gap: 1rem; + } + h1 { + font-size: 2rem; + } + h1, + .destructive-action-text { + color: var(--JTCS-danger-color); + font-weight: 700; + } + } +} diff --git a/styles/_variables.scss b/styles/_variables.scss new file mode 100644 index 0000000..27924c0 --- /dev/null +++ b/styles/_variables.scss @@ -0,0 +1,32 @@ +@use "colors" as *; +@use "spacing" as *; +:root { + --foundry-dark-gray: rgba(52, 52, 52, 0.85); + --foundry-purple-background-hover: rgba(60, 0, 120, 0.5); + --foundry-purple-shadow-hover: #9b8dff; + + --foundry-purple-border: #3b1893; + --foundry-purple-background: rgba(30, 0, 60, 0.5); + --box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.36) inset; + $themeColours: ( + "pink": #dc51ac, + "red": #d64651, + "orange": #e55937, + ); + + @each $themeColour, $i in $themeColours { + body { + &.#{$themeColour} { + background-color: $i; + } + } + } + @each $color, $shades in $colors { + @each $shade, $value in $shades { + --color-#{$color}-#{$shade}: #{$value}; + } + } + @each $size, $value in $spacing { + --space-#{$size}: #{$value}; + } +} diff --git a/styles/styles.css b/styles/styles.css new file mode 100644 index 0000000..7fed6f8 --- /dev/null +++ b/styles/styles.css @@ -0,0 +1,2859 @@ +@charset "UTF-8"; +:root { + --foundry-dark-gray: rgba(52, 52, 52, 0.85); + --foundry-purple-background-hover: rgba(60, 0, 120, 0.5); + --foundry-purple-shadow-hover: #9b8dff; + --foundry-purple-border: #3b1893; + --foundry-purple-background: rgba(30, 0, 60, 0.5); + --box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.36) inset; + --color-secondary-darkest: hsl(221deg, 98%, 20%); + --color-secondary-800: hsl(215deg, 98%, 30%); + --color-secondary-700: hsl(210deg, 98%, 40%); + --color-secondary-600: hsl(205deg, 98%, 52%); + --color-secondary-base: hsl(199deg, 98%, 63%); + --color-secondary-400: hsl(190deg, 99%, 70%); + --color-secondary-300: hsl(185deg, 100%, 80%); + --color-secondary-lightest: hsl(175deg, 100%, 90%); + --color-primary-darkest: hsl(240deg, 58%, 28%); + --color-primary-800: hsl(240deg, 58%, 30%); + --color-primary-700: hsl(240deg, 58%, 40%); + --color-primary-600: hsl(240deg, 60%, 50%); + --color-primary-base: hsl(240deg, 60%, 60%); + --color-primary-400: hsl(240deg, 60%, 68%); + --color-primary-300: hsl(240deg, 65%, 75%); + --color-primary-200: hsl(240deg, 75%, 85%); + --color-primary-lightest: hsl(240deg, 86%, 92%); + --color-deep-purple-darkest: hsl(269deg, 100%, 8%); + --color-deep-purple-700: hsl(269deg, 100%, 18%); + --color-deep-purple-600: hsl(269deg, 100%, 28%); + --color-deep-purple-base: hsl(269deg, 100%, 38%); + --color-neutral-darkest: hsl(240deg, 17%, 20%); + --color-neutral-700: hsl(240deg, 17%, 30%); + --color-neutral-base: hsl(240deg, 12%, 50%); + --color-neutral-300: hsl(240deg, 17%, 70%); + --color-neutral-lightest: hsl(240deg, 17%, 90%); + --color-glass-no-blur: hsla(0deg, 0%, 44%, 0.478); + --color-glass-blur: hsla(0deg, 0%, 44%, 0.267); + --color-glass-border: hsla(0deg, 0%, 100%, 0.198); + --color-glass-no-blur-dark: hsla(240deg, 11%, 14%, 0.769); + --color-glass-blur-dark: hsla(240deg, 11%, 14%, 0.161); + --color-glass-border-dark: hsla(240deg, 10%, 40%, 0.568); + --color-danger-dark: hsl(0deg, 89%, 20%); + --color-danger-base: hsl(0deg, 59%, 50%); + --color-danger-light: hsl(0deg, 99%, 70%); + --color-warning-dark: hsl(28deg, 100%, 30%); + --color-warning-base: hsl(23deg, 80%, 50%); + --color-warning-light: hsl(21deg, 100%, 65%); + --color-success-dark: hsl(100deg, 100%, 30%); + --color-success-base: hsl(100deg, 100%, 50%); + --color-success-light: hsl(100deg, 100%, 70%); + --color-info-dark: hsl(200deg, 100%, 25%); + --color-info-base: hsl(200deg, 60%, 40%); + --color-info-light: hsl(200deg, 80%, 60%); + --space-small: 1rem; + --space-medium: 2rem; + --space-large: 3rem; +} +:root body.pink { + background-color: #dc51ac; +} +:root body.red { + background-color: #d64651; +} +:root body.orange { + background-color: #e55937; +} + +.blank-button, #JTCS-custom-dialog .dialog-buttons .dialog-button { + border: none; + background: transparent; + margin: unset; + padding: unset; +} + +@-webkit-keyframes fade { + from { + opacity: 0%; + } + to { + opacity: 100%; + } +} + +@keyframes fade { + from { + opacity: 0%; + } + to { + opacity: 100%; + } +} +@-webkit-keyframes fadeOut { + from { + opacity: 100%; + } + to { + opacity: 0%; + } +} +@keyframes fadeOut { + from { + opacity: 100%; + } + to { + opacity: 0%; + } +} +:root { + --bg-color-hover: var(--color-border-dark-primary); + --bg-color: hsla(0, 0%, 20%, 0.9); + --bg-color-toggle: var(--foundry-purple-background); + --bg-color-toggle-hover: var(--foundry-purple-background-hover); + --border-color-toggle: var(--foundry-purple-border); + --shadow-color-toggle-hover: var(--foundry-purple-shadow-hover); + --color-text-primary: var(--color-text-dark-primary); + --color-text-secondary: var(--color-text-light-0); + --color-text-secondary-hover: var(--color-text-light-highlight); + --shadow-color: var(--color-shadow-dark); + --shadow-color-hover: var(--color-shadow-highlight); + --border-color: var(--color-border-dark); + --border-color-hover: var(--color-border-highlight); + --border-color-hover-alt: var(--color-border-highlight-alt); + --accent-color-1: var(--foundry-purple-shadow-hover, #9b8dff); + --accent-color-2: var(--color-border-highlight); + --color-accent: var(--foundry-purple-background); +} + +.popover { + position: absolute; + border-top: 4px solid var(--JTCS-accent-color); + padding: var(--padding-small); + background-color: var(--JTCS-elevation-BG-color); + color: var(--JTCS-text-color-on-bg); + top: 100%; + min-width: -webkit-max-content; + min-width: -moz-max-content; + min-width: max-content; + box-shadow: var(--box-shadow); +} +.popover select { + color: var(--color-text-light-0); +} +.popover input[type=text] { + color: var(--color-text-dark-primary, black) !important; +} +.popover[data-variant=over] { + bottom: 100%; +} + +.fill-parent { + width: 100%; + height: 100%; +} + +.reset-margin, .clickableImageContainer, .item-menu hr.list-divider { + margin: 0; +} + +.reset-padding { + padding: 0; +} + +.full-parent-height { + height: 100%; +} + +.flow > * + *, #JTCSSettingsApplication .window-content .flow > * + *, +#slideshow-config .window-content .flow > * + *, +#JTCS-custom-dialog .window-content .flow > * + * { + margin-top: var(--flow-space, 1em); +} + +.img-button { + background: none; + background-color: transparent; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; +} +.img-button img { + max-width: 3rem; +} +.img-button.framed { + position: relative; +} +.img-button.framed img { + -webkit-clip-path: circle(50%); + clip-path: circle(50%); +} +.img-button.framed:before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + background-image: url("/modules/hud-and-trackers/images/Button Badges/Frames & Borders/circle-frame.webp"); +} + +.danger { + background-color: var(--JTCS-danger-color); + color: var(--JTCS-text-color-on-fill); +} + +.danger-text { + color: var(--JTCS-danger-color); +} + +.toggle, +.toggle.active { + --bg-color: var(--bg-color-toggle); + --shadow-color-hover: var(--shadow-color-toggle-hover); + --border-color-hover: var(--border-color-toggle); +} +.toggle:focus, +.toggle.active:focus { + --color-shadow-primary: #3a00ae; +} + +.button-container { + position: relative; + list-style-type: none; +} +.button-container .nested-buttons { + display: flex; + position: absolute; + top: 0; + left: 100%; +} +.button-container .nested-buttons button { + white-space: nowrap; +} + +.floating-control { + color: var(--color-text-secondary); + background-color: var(--bg-color); + box-shadow: 0 0 10px var(--shadow-color); + border: 1px solid var(--border-color); +} +.floating-control i { + margin: 0; +} +.floating-control:hover, .floating-control.active { + cursor: pointer; + box-shadow: 0 0 10px var(--shadow-color-hover); + border: 1px solid var(--border-color-hover); + color: var(--color-text-secondary-hover); +} + +.fade-to-hidden { + opacity: 0%; + transition: opacity 500ms ease-out; +} + +.JTCS-hidden, #sheet-controls button.JTCS-hidden, .input-span[data-variant=visible-tick] input[type=radio], +.input-span[data-variant=visible-tick] input[type=checkbox], input:is([type=radio], [type=checkbox]).hidden-input { + transform: scale(0); + position: absolute; + overflow: hidden; + clip: rect(0 0 0 0); + height: 1px; + width: 1px; + margin: -1px; + padding: 0; + border: 0; +} + +input:is([type=radio], [type=checkbox]).hidden-input + label { + transform: scale(1); + color: var(--color-text-secondary); + background-color: var(--bg-color); + box-shadow: 0 0 10px transparent; + border: 1px solid var(--border-color); + padding: 0.5rem 0.5rem; + overflow: visible; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + width: -webkit-max-content; + width: -moz-max-content; + width: max-content; + display: flex; + justify-content: center; + align-items: center; + transition: background-color 150ms ease-out transform 150ms ease-out; +} +input:is([type=radio], [type=checkbox]).hidden-input + label:hover { + cursor: pointer; + border: 1px solid var(--border-color-hover); + color: var(--color-text-secondary-hover); + box-shadow: 0 0 10px var(--shadow-color-hover); +} +input:is([type=radio], [type=checkbox]).hidden-input:checked + label { + transform: scale(1.04); + z-index: 1; + color: var(--color-text-secondary-hover); + box-shadow: 0px 0px 10px var(--shadow-color-hover); + border: 1px solid var(--color-border-highlight-alt); + transition: background-color 250ms ease-out; +} +input:is([type=radio], [type=checkbox]).hidden-input:checked + label:hover { + color: var(--shadow-color-hover); +} +input:is([type=radio], [type=checkbox]).hidden-input:checked + label .tooltip { + box-shadow: 0px 0px 10px var(--shadow-color-hover); + border: 1px solid var(--color-border-highlight-alt); +} + +.minimize { + transform: scaleY(0); + opacity: 0; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + pointer-events: auto; + display: flex; + flex-direction: column; + transform-origin: top center; + transition: transform 100ms ease-out, opacity 100ms ease-out; +} + +.input-span { + display: inline-flex; +} +.input-span input:disabled + label { + opacity: 50%; +} +.input-span[data-variant=visible-tick] input[type=radio] + label, +.input-span[data-variant=visible-tick] input[type=checkbox] + label { + background-color: transparent; + color: var(--JTCS-accent-color); + border: 2px solid var(--JTCS-accent-color); + transition: background-color 120ms ease-in, color 120ms ease-in; +} +.input-span[data-variant=visible-tick] input[type=radio] + label:hover, .input-span[data-variant=visible-tick] input[type=radio] + label:focus, +.input-span[data-variant=visible-tick] input[type=checkbox] + label:hover, +.input-span[data-variant=visible-tick] input[type=checkbox] + label:focus { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); +} +.input-span[data-variant=visible-tick] input[type=radio] + label .tick-circle, +.input-span[data-variant=visible-tick] input[type=radio] + label .tick-box, +.input-span[data-variant=visible-tick] input[type=checkbox] + label .tick-circle, +.input-span[data-variant=visible-tick] input[type=checkbox] + label .tick-box { + border: 1px solid var(--JTCS-accent-color); + display: inline-flex; + width: 1rem; + height: 1rem; + background-color: var(--JTCS-background-color); +} +.input-span[data-variant=visible-tick] input[type=radio] + label .tick-circle, +.input-span[data-variant=visible-tick] input[type=checkbox] + label .tick-circle { + border-radius: 50%; +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label { + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-accent-color); + border: 2px solid var(--JTCS-accent-color); + outline: 1.5px solid transparent; + outline-offset: -1px; + transition: outline-color 200ms ease-out, box-shadow 200ms ease-out; +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label:hover, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label:hover { + outline-color: white; + box-shadow: 0px 0px 4px 2px var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-circle, +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-box, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-circle, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-box { + position: relative; +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-circle:after, +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-box:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-circle:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-box:after { + position: absolute; + content: ""; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-circle:after, +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-box:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-circle:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-box:after { + width: 0.5rem; + height: 0.5rem; +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-circle:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-circle:after { + border-radius: 50%; + background-color: var(--JTCS-accent-color); +} +.input-span[data-variant=visible-tick] input[type=radio]:checked + label .tick-box:after, +.input-span[data-variant=visible-tick] input[type=checkbox]:checked + label .tick-box:after { + content: "✓"; + color: var(--JTCS-accent-color); + top: unset; +} + +p.inline-notification { + display: inline-flex; +} +p.inline-notification[data-variant=info] { + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-info-color); +} +p.inline-notification[data-variant=info] .icon-span i { + color: var(--color-info-dark); + margin-right: 0.25rem; +} +p.inline-notification[data-variant=warning] { + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-warning-color); +} +p.inline-notification[data-variant=warning] .icon-span i { + color: var(--color-warning-dark); + margin-right: 0.25rem; +} +p.inline-notification[data-variant=danger] { + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-danger-color); +} +p.inline-notification[data-variant=danger] .icon-span i { + color: var(--color-danger-dark); + margin-right: 0.25rem; +} +p.inline-notification[data-variant=success] { + color: var(--JTCS-text-color-on-fill); + background-color: var(--JTCS-success-color); +} +p.inline-notification[data-variant=success] .icon-span i { + color: var(--color-success-dark); + margin-right: 0.25rem; +} + +input[type=radio]:checked.accent-input { + --accent-color: var(--accent-color-1); + accent-color: var(--accent-color); + filter: unset; + -webkit-filter: unset; +} + +.fit-content-width { + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; +} + +.hover-reveal { + position: relative; +} +.hover-reveal > .hover-reveal--child { + opacity: 0; + transition: opacity 250ms ease-in; +} +.hover-reveal:hover .hover-reveal--child { + opacity: 100%; +} + +.hover-reveal-right { + position: relative; +} +.hover-reveal-right:hover .hover-reveal-right-child { + opacity: 100%; +} +.hover-reveal-right-child { + position: absolute; + opacity: 0; + left: 100%; + top: 0; +} + +.hover-reveal-up { + position: relative; +} +.hover-reveal-up:hover .hover-reveal-up-child, .hover-reveal-up.open .hover-reveal-up-child { + opacity: 1; +} +.hover-reveal-up-child { + position: absolute; + bottom: 100%; + left: 0; + transform-origin: bottom center; + opacity: 0; + transition: opacity 150ms ease-out backdrop-filter 150ms ease-out; +} + +fieldset.toggle-button-group { + max-width: -webkit-fit-content; + max-width: -moz-fit-content; + max-width: fit-content; + box-shadow: var(--box-shadow); + pointer-events: auto; + display: flex; + flex-direction: row; + border: none; + color: currentColor; + padding: 0rem 0rem; + min-width: 50%; +} +fieldset.toggle-button-group * { + pointer-events: auto; +} +fieldset.toggle-button-group legend { + background-color: inherit; + padding: 0.5rem; +} +fieldset.toggle-button-group label:first-of-type { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +fieldset.toggle-button-group label:last-of-type { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +.full-width { + width: 100%; +} + +.interactive { + cursor: pointer; +} + +.fade { + background: rgba(255, 255, 255, var(--journal-fade, 50%)) !important; +} +.fade[data-fade-all] { + opacity: var(--journal-fade, 50%); +} +.fade[data-fade-all] .editor-content img { + opacity: var(--journal-fade, 50%) !important; +} + +#slideshow-config .fade { + background: var(--JTCS-background-color) !important; +} + +.padding-top-small { + padding-top: 0.5rem; +} + +.padding-left-small { + padding-left: 0.5rem; +} + +.padding-bottom-small { + padding-bottom: 0.5rem; +} + +.padding-right-small { + padding-right: 0.5rem; +} + +.padding-inline-small { + padding-inline: 0.5rem; +} + +.padding-block-small { + padding-block: 0.5rem; +} + +.padding-top-medium { + padding-top: 1rem; +} + +.padding-left-medium { + padding-left: 1rem; +} + +.padding-bottom-medium { + padding-bottom: 1rem; +} + +.padding-right-medium { + padding-right: 1rem; +} + +.padding-inline-medium { + padding-inline: 1rem; +} + +.padding-block-medium { + padding-block: 1rem; +} + +.padding-top-large { + padding-top: 1.5rem; +} + +.padding-left-large { + padding-left: 1.5rem; +} + +.padding-bottom-large { + padding-bottom: 1.5rem; +} + +.padding-right-large { + padding-right: 1.5rem; +} + +.padding-inline-large { + padding-inline: 1.5rem; +} + +.padding-block-large { + padding-block: 1.5rem; +} + +.padding-small, #slideshow-config .empty-state, +.clickableImageContainer .empty-state { + padding: 0.25rem 0.5rem; +} + +.padding-medium, .clickableImageContainer { + padding: 0.5rem 1rem; +} + +.padding-large, #JTCS-custom-dialog .dialog-content { + padding: 1rem 1.5rem; +} + +.padding-none { + padding: 0; +} + +.margin-none { + margin: 0; +} + +button { + cursor: pointer; +} + +button.icon-button { + height: 0; + padding: 0; + margin: 0; + border: none; + background: none; + background-color: transparent; + width: 35%; + padding-bottom: 35%; + position: relative; +} +button.icon-button i { + pointer-events: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} +button.icon-button[data-variant=small] { + width: 5%; + padding-bottom: 5%; +} +button.icon-button.active { + background-color: var(--JTCS-accent-color); + box-shadow: 0px 0px 8px 2px var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +button.icon-button.active:hover, button.icon-button.active:focus { + transform: scale(1.12); +} + +.text-align-left { + text-align: left; +} + +.text-align-center { + text-align: center; +} + +.text-align-right { + text-align: right; +} + +.hover-text-color:hover { + color: var(--color-accent); +} + +.bg-fill { + background-color: var(--bg-color); + color: var(--text-color); +} + +.bg-ghost { + background-color: transparent; + border: 2px solid var(--border-color); + color: var(--text-color); +} + +.bg-dark { + background-color: var(--bg-color); + color: var(--color-text-secondary); +} + +.bg-primary { + background-color: var(--bg-color); +} + +.bg-hover { + background-color: var(--bg-color-hover); +} + +.bg-hover-toggle { + background-color: var(--bg-color-toggle-hover); +} + +.bg-none { + background: none; + background-color: transparent; +} + +.bg-neutral-base { + background-color: var(--color-neutral-base); +} + +.bg-neutral-darkest { + background-color: var(--color-neutral-darkest); +} + +.bg-neutral-lightest { + background-color: var(--color-neutral-lightest); +} + +.bg-glass { + transition: background-color 100ms ease-in backdrop-filter 100ms ease-in; + background-color: var(--color-glass-no-blur); + border: 1px solid var(--color-glass-border); +} +@supports ((-webkit-backdrop-filter: blur(12px)) or (backdrop-filter: blur(12px))) { + .bg-glass { + background-color: var(--color-glass-blur); + -webkit-backdrop-filter: blur(12px) opacity(1); + backdrop-filter: blur(12px) opacity(1); + } +} + +.hover-grow { + transition: transform 150ms ease-in; +} +.hover-grow:hover { + transform: scale(1.2); +} + +.hover-bg-primary { + transition: background-color 150ms ease-in; +} +.hover-bg-primary:hover { + background-color: var(--color-secondary-base); +} + +.hover-bg-secondary { + transition: background-color 150ms ease-in; +} +.hover-bg-secondary:hover { + background-color: var(--color-secondary-base); +} + +.hover-bg-removable { + transition: background-color 150ms ease-in; +} +.hover-bg-removable:hover { + background-color: var(--JTCS-danger-color); +} + +.hover-bg-none:hover { + background: none; + background-color: transparent; +} + +.hover-bg-dynamic, .item-menu li { + transition: background-color 150ms ease-in; +} +.hover-bg-dynamic:hover, .item-menu li:hover { + background-color: var(--bg-color); +} + +.hover-border { + border: 2px solid transparent; + transition: border-color 150ms ease-in; +} +.hover-border[data-variant=bottom] { + border: unset; + transition: border-color 150ms ease-in; + border-bottom: 2px solid var(--JTCS-background-color-10); +} +.hover-border:hover { + border-color: var(--accent-color); + transition: border-color 150ms ease-out; +} +.hover-border:hover[data-variant=bottom] { + border: unset; + border-bottom: 2px solid; +} + +.border-bottom { + border: unset; + border-bottom: 2px solid var(--JTCS-background-color-20); +} + +.focus-border { + outline: 3px solid transparent; + transition: outline-color 150ms ease-in; +} +.focus-border:focus, .focus-border:focus-within { + outline-color: var(--accent-color); + transition: outline-color 150ms ease-out; +} + +.focus-bg-none:focus { + background: none; + background-color: transparent; +} + +.hover-disabled:disabled { + cursor: not-allowed; +} + +.brighten-on-open.open { + filter: brightness(1.2); +} + +.glow-on-open.open { + filter: drop-shadow(0 0 0.75rem crimson); +} + +.child-glow-on-open.open > img { + filter: drop-shadow(0 0 0.75rem crimson); +} + +.drop-shadow { + filter: drop-shadow(2px 2px 3px rgba(0, 0, 0, 0.35)); +} + +.max-height-full-parent { + max-height: 100%; +} + +.text-shadow { + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5); +} + +.shadow-none { + box-shadow: var(--shadow-none); +} + +.shadow-lowest { + box-shadow: var(--shadow-lowest); +} + +.shadow-base { + box-shadow: var(--shadow-base); +} + +.shadow-highest { + box-shadow: var(--shadow-highest); +} + +.shadow-circle { + filter: drop-shadow(2px 4px 6px black); + transition: filter 150ms ease-in; +} + +.shadow-inset { + box-shadow: var(--box-shadow-inset); +} + +.overflow-auto { + overflow: auto; +} +.overflow-auto-x { + overflow-x: auto; +} +.overflow-auto-y { + overflow-y: auto; +} +.overflow-scroll { + overflow: scroll; +} +.overflow-scroll-x { + overflow-x: scroll; +} +.overflow-scroll-y { + overflow-y: scroll; +} +.overflow-hidden, .item-menu { + overflow: hidden; +} +.overflow-hidden-x { + overflow-x: hidden; +} +.overflow-hidden-y { + overflow-y: hidden; +} + +.overflow-auto-y { + overflow-y: auto; +} + +.scroll-y { + overflow-y: scroll; +} + +.scroll-x { + overflow-x: scroll; +} + +.hidden-overflow-y { + overflow-y: hidden; +} + +.hidden-overflow-x { + overflow-x: hidden; +} + +.border-color-shadow { + --border-color: rgba(0, 0, 0, 0.25); +} + +.border-block { + border-block: 2px solid var(--border-color); +} + +.border-inline { + border-inline: 2px solid var(--border-color); +} + +.border-full { + border: 2px solid var(--border-color); +} + +.square { + width: 2rem; + height: 2rem; +} + +.square-medium { + width: 4rem; + height: 4rem; +} + +.square-large { + width: 6rem; + height: 6rem; +} + +.before-color-overlay:before { + content: ""; + position: absolute; + width: 100%; + height: 100%; + background-color: var(--bg-color); + mix-blend-mode: color; + opacity: 20%; + transition: opacity 100ms ease-in; +} + +.before-hover-opacity:hover:before { + opacity: 100%; +} + +.rounded-outer-children:first-child button { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +.rounded-outer-children:last-child button { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +.horizontal-button-wrapper { + display: flex; + align-items: center; + justify-content: space-evenly; +} + +.button-list { + display: flex; +} +.button-list li { + list-style-type: none; +} + +.sub-section { + background-color: var(--bg-color); + border-radius: 10px; +} + +[data-title] { + position: relative; +} +[data-title] .tooltip { + font-size: var(--font-size-12); + transition-delay: 0ms; + transition: opacity 170ms ease-out; + pointer-events: none; + opacity: 0%; + position: absolute; + top: 100%; + left: 0; + min-width: calc(100% + 10ch); + max-width: 210%; +} +[data-title] .tooltip.tooltip-right { + left: unset; + top: 100%; + right: 100%; +} +[data-title] .tooltip.tooltip-wide { + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + max-width: unset; +} +[data-title] .tooltip span { + pointer-events: none; + white-space: nowrap; +} +[data-title] .tooltip span:first-of-type { + color: var(--accent-color-1); +} +[data-title] .tooltip span:last-of-type { + color: var(--accent-color-2); +} +[data-title]:hover .tooltip { + opacity: 100%; + transition: opacity 320ms ease-out; + transition-delay: 500ms; +} + +.header { + display: flex; + flex-direction: column; + justify-content: center; + gap: 1rem; +} + +.config-wrapper { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 1rem; +} + +.drawer-visible { + right: unset; + left: 0px; +} + +.side-drawer { + position: absolute; + right: 100%; + z-index: 200; + width: 50%; +} +.side-drawer .tab { + position: absolute; + left: 100%; + top: 50%; + width: 3rem; + height: 3rem; + background-color: var(--color-secondary-base); + color: white; + z-index: 200; +} + +.justify-self-right, .justify-self-center { + margin-left: auto; +} + +.justify-self-left, .justify-self-center { + margin-right: auto; +} + +.has-floating-label { + box-sizing: border-box; + position: relative; + --form-field-height: fit-content; +} +.has-floating-label .floating-label { + color: var(--foundry-dark-gray); + position: absolute; + top: 0.1em; + left: 0.5em; + background: inherit; + font-size: smaller; +} +.has-floating-label input, +.has-floating-label select { + padding-top: 1.25em; + padding-bottom: 0.25em; +} + +.flex-row { + display: flex; + flex-direction: row; +} + +.flex-column { + display: flex; + flex-direction: column; +} + +.flex-wrap { + flex-wrap: wrap; +} +.flex-nowrap { + flex-wrap: nowrap; +} + +.flex-centered, #JTCS-custom-dialog .destructive-action-prompt { + display: flex; + justify-content: center; + align-items: center; +} + +.flex-justify-center { + justify-content: center; +} +.flex-justify-stretch { + justify-content: stretch; +} +.flex-justify-space-between { + justify-content: space-between; +} +.flex-justify-space-evenly { + justify-content: space-evenly; +} +.flex-justify-start { + justify-content: flex-start; +} +.flex-justify-end { + justify-content: flex-end; +} + +.flex-align-center { + align-items: center; +} +.flex-align-start { + align-items: flex-start; +} + +.gap-small { + gap: 0.25rem; +} + +.gap-medium { + gap: 0.5rem; +} + +.gap-large { + gap: 1rem; +} + +.has-absolute-child { + position: relative; +} +.has-absolute-child .absolute--child { + position: absolute; +} + +.focus-reveal > .focus-reveal--child { + opacity: 0; +} +.focus-reveal > .focus-reveal--child[data-variant=scale] { + transform: scale(0); + transition: transform scale 150ms; +} +.focus-reveal:focus > .focus-reveal--child, .focus-reveal:focus-within > .focus-reveal--child { + opacity: 100%; +} +.focus-reveal:focus > .focus-reveal--child[data-variant=scale], .focus-reveal:focus-within > .focus-reveal--child[data-variant=scale] { + transform: scale(1); +} + +.chip-span { + display: inline-flex; + justify-content: space-evenly; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + width: 100%; + gap: 0.5rem; + padding: 0.5rem; +} +.chip-span label { + border-radius: 999px; + min-width: 13ch; +} + +.wrap { + flex-wrap: wrap; +} + +.text-centered { + text-align: center; +} + +.ghost { + background-color: transparent; + color: var(--color-primary-base); + border: 1px solid white; + border-color: var(--color-primary-base); +} +.ghost:hover { + border-color: var(--color-secondary-base); + color: var(--color-secondary-base); +} + +.filled { + background-color: var(--color-primary-base); + border-color: var(--color-primary-base); + color: white; + transition: border-color 100ms ease-in, background-color 100ms ease-in; +} +.filled:hover { + background-color: var(--color-secondary-base); + border-color: var(--color-secondary-base); +} + +.remove-on-click { + position: relative; +} +.remove-on-click:before { + position: absolute; + pointer-events: none; + width: 100%; + height: 100%; + border-radius: inherit; + background-color: rgb(255, 92, 63); + content: "Remove?"; + opacity: 0; + transition: opacity 100ms ease-in; +} +.remove-on-click:hover:before { + opacity: 100%; +} + +.bg-accent { + background-color: var(--accent-color); +} + +.sharp-corners { + border-radius: 0px; +} + +.rounded { + border-radius: 8px; +} + +.slight-rounded { + border-radius: 5px; +} + +.cap-rounded { + border-radius: 999px; +} + +.single-sharp-corner { + border-top-left-radius: 0; +} + +.text-white { + color: white; +} + +.text-warning { + color: rgb(255, 92, 63); +} + +.text-danger { + color: rgb(255, 92, 63); +} + +.text-primary { + color: var(--color-primary-base); +} + +.text-accent { + color: var(--color-accent); +} + +.text-heavy { + font-weight: bold; +} + +.border-styled { + border: 2px solid var(--border-color); +} +.border-styled[data-variant=bottom] { + border: unset; + border-bottom: 2px solid var(--border-color); +} + +.border-primary { + border-color: var(--color-primary-base); +} + +.border-accent { + border-color: var(--color-accent); +} + +.max-height-50 { + max-height: 50%; +} + +.hover-shadow:before { + pointer-events: none; + position: absolute; + width: 100%; + height: 100%; + content: ""; + border-radius: inherit; + box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.25); + opacity: 0%; + transition: opacity 100ms ease-out; +} +.hover-shadow:hover:before { + opacity: 100%; +} + +.window-padding { + padding: clamp(0.5rem, 0.5rem + 1vw, 1rem) clamp(0.5rem, 0.5rem + 1vh, 1rem); +} + +.window-gap { + display: flex; + flex-direction: column; + gap: 1rem; +} + +.input-and-label-wrapper { + display: flex; + flex-direction: column; + gap: 0.5rem; +} + +.file-input-wrapper { + display: flex; + max-width: 100%; +} +.file-input-wrapper button { + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: space-evenly; + height: 100%; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.file-input-wrapper input { + flex: 1 0 auto; + max-width: 60%; + height: 100%; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} + +.capitalize { + text-transform: capitalize; +} + +.item-menu { + display: flex; + justify-content: space-between; + flex-direction: column; +} +.item-menu hr.list-divider { + width: 100%; + border-bottom: unset; + border-top: unset; + border: 1px solid var(--JTCS-accent-color); + margin-top: unset; + margin-bottom: unset; +} +.item-menu li { + color: var(--JTCS-text-color-on-bg); + background-color: var(--JTCS-elevation-BG-color); + width: 100%; + --bg-color: var(--JTCS-accent-color); + --text-color: var(--JTCS-text-color-on-fill); +} +.item-menu li:first-child { + margin-top: 1em; +} +.item-menu li:last-child { + padding-top: 1em; + padding-bottom: 1em; +} +.item-menu li:hover { + color: var(--JTCS-text-color-on-fill); +} +.item-menu li i { + display: grid; + place-content: center; + margin-right: 1.5rem; +} + +#JTCS-custom-dialog { + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; +} +#JTCS-custom-dialog .window-content h1 { + font-size: 1.5rem; + border-bottom: unset; +} +#JTCS-custom-dialog .window-content input { + width: 100%; +} +#JTCS-custom-dialog .dialog-content { + display: flex; + flex-direction: column; + align-items: center; + color: var(--JTCS-text-color-on-bg); +} +#JTCS-custom-dialog .dialog-buttons { + flex: 0; + display: flex; + gap: 0.5rem; +} +#JTCS-custom-dialog.dialog-grid .dialog-buttons { + display: grid; + grid-template-columns: repeat(4, 1fr); +} +#JTCS-custom-dialog.dialog-grid-3 .dialog-buttons { + display: grid; + grid-template-columns: repeat(3, 1fr); +} +#JTCS-custom-dialog .dialog-buttons .dialog-button [data-button=delete], +#JTCS-custom-dialog .dialog-buttons .dialog-button [data-button=reset] { + background: var(--JTCS-danger-color); + color: white; + font-weight: bold; +} +#JTCS-custom-dialog .dialog-buttons .dialog-button { + background-color: transparent; + color: var(--JTCS-accent-color); + border: 2px solid var(--JTCS-accent-color); + transition: background-color 120ms ease-in, color 120ms ease-in; +} +#JTCS-custom-dialog .dialog-buttons .dialog-button:hover, #JTCS-custom-dialog .dialog-buttons .dialog-button:focus, #JTCS-custom-dialog .dialog-buttons .dialog-button.active { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); +} +#JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=delete], #JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=reset] { + background-color: var(--JTCS-danger-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; + color: var(--JTCS-text-color-on-fill); + font-weight: bold; +} +#JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=delete]:hover, #JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=delete]:focus, #JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=reset]:hover, #JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=reset]:focus { + transform: scale(1.12); +} +#JTCS-custom-dialog .dialog-buttons .dialog-button[data-button=list] { + background: !important; +} +#JTCS-custom-dialog .destructive-action-prompt { + display: flex; + flex-direction: column; + flex: 1 0 0; +} +#JTCS-custom-dialog .destructive-action-prompt h1 { + display: inline-flex; + gap: 1rem; +} +#JTCS-custom-dialog .destructive-action-prompt h1 { + font-size: 2rem; +} +#JTCS-custom-dialog .destructive-action-prompt h1, +#JTCS-custom-dialog .destructive-action-prompt .destructive-action-text { + color: var(--JTCS-danger-color); + font-weight: 700; +} + +:root { + --foundry-dark-gray: rgba(52, 52, 52, 0.85); + --foundry-purple-background-hover: rgba(60, 0, 120, 0.5); + --foundry-purple-shadow-hover: #9b8dff; + --foundry-purple-border: #3b1893; + --foundry-purple-background: rgba(30, 0, 60, 0.5); + --box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.36) inset; + --color-secondary-darkest: hsl(221deg, 98%, 20%); + --color-secondary-800: hsl(215deg, 98%, 30%); + --color-secondary-700: hsl(210deg, 98%, 40%); + --color-secondary-600: hsl(205deg, 98%, 52%); + --color-secondary-base: hsl(199deg, 98%, 63%); + --color-secondary-400: hsl(190deg, 99%, 70%); + --color-secondary-300: hsl(185deg, 100%, 80%); + --color-secondary-lightest: hsl(175deg, 100%, 90%); + --color-primary-darkest: hsl(240deg, 58%, 28%); + --color-primary-800: hsl(240deg, 58%, 30%); + --color-primary-700: hsl(240deg, 58%, 40%); + --color-primary-600: hsl(240deg, 60%, 50%); + --color-primary-base: hsl(240deg, 60%, 60%); + --color-primary-400: hsl(240deg, 60%, 68%); + --color-primary-300: hsl(240deg, 65%, 75%); + --color-primary-200: hsl(240deg, 75%, 85%); + --color-primary-lightest: hsl(240deg, 86%, 92%); + --color-deep-purple-darkest: hsl(269deg, 100%, 8%); + --color-deep-purple-700: hsl(269deg, 100%, 18%); + --color-deep-purple-600: hsl(269deg, 100%, 28%); + --color-deep-purple-base: hsl(269deg, 100%, 38%); + --color-neutral-darkest: hsl(240deg, 17%, 20%); + --color-neutral-700: hsl(240deg, 17%, 30%); + --color-neutral-base: hsl(240deg, 12%, 50%); + --color-neutral-300: hsl(240deg, 17%, 70%); + --color-neutral-lightest: hsl(240deg, 17%, 90%); + --color-glass-no-blur: hsla(0deg, 0%, 44%, 0.478); + --color-glass-blur: hsla(0deg, 0%, 44%, 0.267); + --color-glass-border: hsla(0deg, 0%, 100%, 0.198); + --color-glass-no-blur-dark: hsla(240deg, 11%, 14%, 0.769); + --color-glass-blur-dark: hsla(240deg, 11%, 14%, 0.161); + --color-glass-border-dark: hsla(240deg, 10%, 40%, 0.568); + --color-danger-dark: hsl(0deg, 89%, 20%); + --color-danger-base: hsl(0deg, 59%, 50%); + --color-danger-light: hsl(0deg, 99%, 70%); + --color-warning-dark: hsl(28deg, 100%, 30%); + --color-warning-base: hsl(23deg, 80%, 50%); + --color-warning-light: hsl(21deg, 100%, 65%); + --color-success-dark: hsl(100deg, 100%, 30%); + --color-success-base: hsl(100deg, 100%, 50%); + --color-success-light: hsl(100deg, 100%, 70%); + --color-info-dark: hsl(200deg, 100%, 25%); + --color-info-base: hsl(200deg, 60%, 40%); + --color-info-light: hsl(200deg, 80%, 60%); + --space-small: 1rem; + --space-medium: 2rem; + --space-large: 3rem; +} +:root body.pink { + background-color: #dc51ac; +} +:root body.red { + background-color: #d64651; +} +:root body.orange { + background-color: #e55937; +} + +:root { + --box-shadow-inset: 1px 1px 3px 1px rgba(0, 0, 0, 0.36) inset; + --box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.36); +} + +#slideshow-config .empty-state, +.clickableImageContainer .empty-state { + background-color: var(--JTCS-elevation-BG-color); + color: var(--JTCS-text-color-on-bg); + text-align: center; +} +#slideshow-config .empty-state span.accent, +.clickableImageContainer .empty-state span.accent { + padding-inline: 0.15rem; + font-weight: bold; +} +#slideshow-config .empty-state[data-type=art] span.accent, +.clickableImageContainer .empty-state[data-type=art] span.accent { + color: var(--data-art-color); +} +#slideshow-config .empty-state[data-type=frame] span.accent, +.clickableImageContainer .empty-state[data-type=frame] span.accent { + color: var(--data-frame-color); +} + +#JTCSSettingsApplication button > i, +#slideshow-config button > i, +#JTCS-custom-dialog button > i { + margin-right: unset; +} +#JTCSSettingsApplication .window-content, +#slideshow-config .window-content, +#JTCS-custom-dialog .window-content { + --color-shadow-primary: var(--JTCS-accent-color); + background: var(--JTCS-background-color); + --flow-space: 2em; +} +#JTCSSettingsApplication .window-content .container, +#slideshow-config .window-content .container, +#JTCS-custom-dialog .window-content .container { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border: 1px solid var(--JTCS-background-color-30); +} +#JTCSSettingsApplication .window-content .elevation-1, +#slideshow-config .window-content .elevation-1, +#JTCS-custom-dialog .window-content .elevation-1 { + background: var(--JTCS-background-color); +} +#JTCSSettingsApplication .window-content .form-group button.danger, #JTCSSettingsApplication .window-content .form-group button.theme, +#JTCSSettingsApplication .window-content .form-group-stacked button.danger, +#JTCSSettingsApplication .window-content .form-group-stacked button.theme, +#JTCSSettingsApplication .window-content .input-span button.danger, +#JTCSSettingsApplication .window-content .input-span button.theme, +#slideshow-config .window-content .form-group button.danger, +#slideshow-config .window-content .form-group button.theme, +#slideshow-config .window-content .form-group-stacked button.danger, +#slideshow-config .window-content .form-group-stacked button.theme, +#slideshow-config .window-content .input-span button.danger, +#slideshow-config .window-content .input-span button.theme, +#JTCS-custom-dialog .window-content .form-group button.danger, +#JTCS-custom-dialog .window-content .form-group button.theme, +#JTCS-custom-dialog .window-content .form-group-stacked button.danger, +#JTCS-custom-dialog .window-content .form-group-stacked button.theme, +#JTCS-custom-dialog .window-content .input-span button.danger, +#JTCS-custom-dialog .window-content .input-span button.theme { + width: 100%; + max-width: 300px; + margin-inline: auto; +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label input, +#JTCSSettingsApplication .window-content .form-group-stacked.has-floating-label input, +#JTCSSettingsApplication .window-content .input-span.has-floating-label input, +#slideshow-config .window-content .form-group.has-floating-label input, +#slideshow-config .window-content .form-group-stacked.has-floating-label input, +#slideshow-config .window-content .input-span.has-floating-label input, +#JTCS-custom-dialog .window-content .form-group.has-floating-label input, +#JTCS-custom-dialog .window-content .form-group-stacked.has-floating-label input, +#JTCS-custom-dialog .window-content .input-span.has-floating-label input { + width: 100%; + max-width: 300px; +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label label, +#JTCSSettingsApplication .window-content .form-group-stacked.has-floating-label label, +#JTCSSettingsApplication .window-content .input-span.has-floating-label label, +#slideshow-config .window-content .form-group.has-floating-label label, +#slideshow-config .window-content .form-group-stacked.has-floating-label label, +#slideshow-config .window-content .input-span.has-floating-label label, +#JTCS-custom-dialog .window-content .form-group.has-floating-label label, +#JTCS-custom-dialog .window-content .form-group-stacked.has-floating-label label, +#JTCS-custom-dialog .window-content .input-span.has-floating-label label { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content .form-group .file-picker-group, +#JTCSSettingsApplication .window-content .form-group-stacked .file-picker-group, +#JTCSSettingsApplication .window-content .input-span .file-picker-group, +#slideshow-config .window-content .form-group .file-picker-group, +#slideshow-config .window-content .form-group-stacked .file-picker-group, +#slideshow-config .window-content .input-span .file-picker-group, +#JTCS-custom-dialog .window-content .form-group .file-picker-group, +#JTCS-custom-dialog .window-content .form-group-stacked .file-picker-group, +#JTCS-custom-dialog .window-content .input-span .file-picker-group { + align-items: stretch; +} +#JTCSSettingsApplication .window-content .form-group .file-picker-group input, +#JTCSSettingsApplication .window-content .form-group-stacked .file-picker-group input, +#JTCSSettingsApplication .window-content .input-span .file-picker-group input, +#slideshow-config .window-content .form-group .file-picker-group input, +#slideshow-config .window-content .form-group-stacked .file-picker-group input, +#slideshow-config .window-content .input-span .file-picker-group input, +#JTCS-custom-dialog .window-content .form-group .file-picker-group input, +#JTCS-custom-dialog .window-content .form-group-stacked .file-picker-group input, +#JTCS-custom-dialog .window-content .input-span .file-picker-group input { + flex: 1; + width: 100%; + max-width: 450px; + border: 1px solid var(--JTCS-accent-color-20); +} +#JTCSSettingsApplication .window-content .form-group .file-picker-group button, +#JTCSSettingsApplication .window-content .form-group-stacked .file-picker-group button, +#JTCSSettingsApplication .window-content .input-span .file-picker-group button, +#slideshow-config .window-content .form-group .file-picker-group button, +#slideshow-config .window-content .form-group-stacked .file-picker-group button, +#slideshow-config .window-content .input-span .file-picker-group button, +#JTCS-custom-dialog .window-content .form-group .file-picker-group button, +#JTCS-custom-dialog .window-content .form-group-stacked .file-picker-group button, +#JTCS-custom-dialog .window-content .input-span .file-picker-group button { + flex: 0; +} +#JTCSSettingsApplication .window-content .form-group.color-picker-container, +#JTCSSettingsApplication .window-content .form-group-stacked.color-picker-container, +#JTCSSettingsApplication .window-content .input-span.color-picker-container, +#slideshow-config .window-content .form-group.color-picker-container, +#slideshow-config .window-content .form-group-stacked.color-picker-container, +#slideshow-config .window-content .input-span.color-picker-container, +#JTCS-custom-dialog .window-content .form-group.color-picker-container, +#JTCS-custom-dialog .window-content .form-group-stacked.color-picker-container, +#JTCS-custom-dialog .window-content .input-span.color-picker-container { + align-items: stretch; +} +#JTCSSettingsApplication .window-content .form-group.color-picker-container input, +#JTCSSettingsApplication .window-content .form-group-stacked.color-picker-container input, +#JTCSSettingsApplication .window-content .input-span.color-picker-container input, +#slideshow-config .window-content .form-group.color-picker-container input, +#slideshow-config .window-content .form-group-stacked.color-picker-container input, +#slideshow-config .window-content .input-span.color-picker-container input, +#JTCS-custom-dialog .window-content .form-group.color-picker-container input, +#JTCS-custom-dialog .window-content .form-group-stacked.color-picker-container input, +#JTCS-custom-dialog .window-content .input-span.color-picker-container input { + width: 100%; + max-width: 300px; +} +#JTCSSettingsApplication .window-content .form-group.color-picker-container .color-picker-group, +#JTCSSettingsApplication .window-content .form-group-stacked.color-picker-container .color-picker-group, +#JTCSSettingsApplication .window-content .input-span.color-picker-container .color-picker-group, +#slideshow-config .window-content .form-group.color-picker-container .color-picker-group, +#slideshow-config .window-content .form-group-stacked.color-picker-container .color-picker-group, +#slideshow-config .window-content .input-span.color-picker-container .color-picker-group, +#JTCS-custom-dialog .window-content .form-group.color-picker-container .color-picker-group, +#JTCS-custom-dialog .window-content .form-group-stacked.color-picker-container .color-picker-group, +#JTCS-custom-dialog .window-content .input-span.color-picker-container .color-picker-group { + flex: 0; + min-width: -webkit-fit-content; + min-width: -moz-fit-content; + min-width: fit-content; +} +#JTCSSettingsApplication .window-content .form-group.color-picker-container .color-actions, +#JTCSSettingsApplication .window-content .form-group-stacked.color-picker-container .color-actions, +#JTCSSettingsApplication .window-content .input-span.color-picker-container .color-actions, +#slideshow-config .window-content .form-group.color-picker-container .color-actions, +#slideshow-config .window-content .form-group-stacked.color-picker-container .color-actions, +#slideshow-config .window-content .input-span.color-picker-container .color-actions, +#JTCS-custom-dialog .window-content .form-group.color-picker-container .color-actions, +#JTCS-custom-dialog .window-content .form-group-stacked.color-picker-container .color-actions, +#JTCS-custom-dialog .window-content .input-span.color-picker-container .color-actions { + flex: 0; +} +#JTCSSettingsApplication .window-content .form-group-stacked, +#slideshow-config .window-content .form-group-stacked, +#JTCS-custom-dialog .window-content .form-group-stacked { + --flow-space: 1em; +} +#JTCSSettingsApplication .window-content .form-group-stacked p.notes, +#slideshow-config .window-content .form-group-stacked p.notes, +#JTCS-custom-dialog .window-content .form-group-stacked p.notes { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content select, +#JTCSSettingsApplication .window-content input, +#JTCSSettingsApplication .window-content label, +#JTCSSettingsApplication .window-content button, +#slideshow-config .window-content select, +#slideshow-config .window-content input, +#slideshow-config .window-content label, +#slideshow-config .window-content button, +#JTCS-custom-dialog .window-content select, +#JTCS-custom-dialog .window-content input, +#JTCS-custom-dialog .window-content label, +#JTCS-custom-dialog .window-content button { + cursor: pointer; +} +#JTCSSettingsApplication .window-content p:not(.inline-notification), +#slideshow-config .window-content p:not(.inline-notification), +#JTCS-custom-dialog .window-content p:not(.inline-notification) { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content .file-picker-group > label, +#slideshow-config .window-content .file-picker-group > label, +#JTCS-custom-dialog .window-content .file-picker-group > label { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content .color-picker-group > label, +#slideshow-config .window-content .color-picker-group > label, +#JTCS-custom-dialog .window-content .color-picker-group > label { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content span:not(.input-span) label, +#slideshow-config .window-content span:not(.input-span) label, +#JTCS-custom-dialog .window-content span:not(.input-span) label { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content li:not(.accent) label, +#slideshow-config .window-content li:not(.accent) label, +#JTCS-custom-dialog .window-content li:not(.accent) label { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content .icon-button:not(.danger):not(.danger-text), +#slideshow-config .window-content .icon-button:not(.danger):not(.danger-text), +#JTCS-custom-dialog .window-content .icon-button:not(.danger):not(.danger-text) { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content h1, +#slideshow-config .window-content h1, +#JTCS-custom-dialog .window-content h1 { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content h2, +#slideshow-config .window-content h2, +#JTCS-custom-dialog .window-content h2 { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content h3, +#slideshow-config .window-content h3, +#JTCS-custom-dialog .window-content h3 { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content h4, +#slideshow-config .window-content h4, +#JTCS-custom-dialog .window-content h4 { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content h1, +#slideshow-config .window-content h1, +#JTCS-custom-dialog .window-content h1 { + border-bottom: unset; + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content h2, +#slideshow-config .window-content h2, +#JTCS-custom-dialog .window-content h2 { + border-bottom: unset; + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content h3, +#slideshow-config .window-content h3, +#JTCS-custom-dialog .window-content h3 { + border-bottom: unset; + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content h4, +#slideshow-config .window-content h4, +#JTCS-custom-dialog .window-content h4 { + border-bottom: unset; + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label input, +#JTCSSettingsApplication .window-content .color-picker-group.has-floating-label input, +#JTCSSettingsApplication .window-content .file-picker-group.has-floating-label input, +#JTCSSettingsApplication .window-content .input-span.has-floating-label input, +#slideshow-config .window-content .form-group.has-floating-label input, +#slideshow-config .window-content .color-picker-group.has-floating-label input, +#slideshow-config .window-content .file-picker-group.has-floating-label input, +#slideshow-config .window-content .input-span.has-floating-label input, +#JTCS-custom-dialog .window-content .form-group.has-floating-label input, +#JTCS-custom-dialog .window-content .color-picker-group.has-floating-label input, +#JTCS-custom-dialog .window-content .file-picker-group.has-floating-label input, +#JTCS-custom-dialog .window-content .input-span.has-floating-label input { + height: 100%; +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label label, +#JTCSSettingsApplication .window-content .color-picker-group.has-floating-label label, +#JTCSSettingsApplication .window-content .file-picker-group.has-floating-label label, +#JTCSSettingsApplication .window-content .input-span.has-floating-label label, +#slideshow-config .window-content .form-group.has-floating-label label, +#slideshow-config .window-content .color-picker-group.has-floating-label label, +#slideshow-config .window-content .file-picker-group.has-floating-label label, +#slideshow-config .window-content .input-span.has-floating-label label, +#JTCS-custom-dialog .window-content .form-group.has-floating-label label, +#JTCS-custom-dialog .window-content .color-picker-group.has-floating-label label, +#JTCS-custom-dialog .window-content .file-picker-group.has-floating-label label, +#JTCS-custom-dialog .window-content .input-span.has-floating-label label { + white-space: nowrap; +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label:hover, +#JTCSSettingsApplication .window-content .color-picker-group.has-floating-label:hover, +#JTCSSettingsApplication .window-content .file-picker-group.has-floating-label:hover, +#JTCSSettingsApplication .window-content .input-span.has-floating-label:hover, +#slideshow-config .window-content .form-group.has-floating-label:hover, +#slideshow-config .window-content .color-picker-group.has-floating-label:hover, +#slideshow-config .window-content .file-picker-group.has-floating-label:hover, +#slideshow-config .window-content .input-span.has-floating-label:hover, +#JTCS-custom-dialog .window-content .form-group.has-floating-label:hover, +#JTCS-custom-dialog .window-content .color-picker-group.has-floating-label:hover, +#JTCS-custom-dialog .window-content .file-picker-group.has-floating-label:hover, +#JTCS-custom-dialog .window-content .input-span.has-floating-label:hover { + --accent-color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label:hover label, +#JTCSSettingsApplication .window-content .color-picker-group.has-floating-label:hover label, +#JTCSSettingsApplication .window-content .file-picker-group.has-floating-label:hover label, +#JTCSSettingsApplication .window-content .input-span.has-floating-label:hover label, +#slideshow-config .window-content .form-group.has-floating-label:hover label, +#slideshow-config .window-content .color-picker-group.has-floating-label:hover label, +#slideshow-config .window-content .file-picker-group.has-floating-label:hover label, +#slideshow-config .window-content .input-span.has-floating-label:hover label, +#JTCS-custom-dialog .window-content .form-group.has-floating-label:hover label, +#JTCS-custom-dialog .window-content .color-picker-group.has-floating-label:hover label, +#JTCS-custom-dialog .window-content .file-picker-group.has-floating-label:hover label, +#JTCS-custom-dialog .window-content .input-span.has-floating-label:hover label { + white-space: nowrap; + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content .form-group.has-floating-label:focus, +#JTCSSettingsApplication .window-content .color-picker-group.has-floating-label:focus, +#JTCSSettingsApplication .window-content .file-picker-group.has-floating-label:focus, +#JTCSSettingsApplication .window-content .input-span.has-floating-label:focus, +#slideshow-config .window-content .form-group.has-floating-label:focus, +#slideshow-config .window-content .color-picker-group.has-floating-label:focus, +#slideshow-config .window-content .file-picker-group.has-floating-label:focus, +#slideshow-config .window-content .input-span.has-floating-label:focus, +#JTCS-custom-dialog .window-content .form-group.has-floating-label:focus, +#JTCS-custom-dialog .window-content .color-picker-group.has-floating-label:focus, +#JTCS-custom-dialog .window-content .file-picker-group.has-floating-label:focus, +#JTCS-custom-dialog .window-content .input-span.has-floating-label:focus { + outline: 1px solid var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content .form-group .input-span, +#JTCSSettingsApplication .window-content .form-group-stacked .input-span, +#slideshow-config .window-content .form-group .input-span, +#slideshow-config .window-content .form-group-stacked .input-span, +#JTCS-custom-dialog .window-content .form-group .input-span, +#JTCS-custom-dialog .window-content .form-group-stacked .input-span { + flex: 1; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + max-width: -webkit-fit-content; + max-width: -moz-fit-content; + max-width: fit-content; +} +#JTCSSettingsApplication .window-content span:not(.input-span):not(.tick-box):not(.tick-circle), +#slideshow-config .window-content span:not(.input-span):not(.tick-box):not(.tick-circle), +#JTCS-custom-dialog .window-content span:not(.input-span):not(.tick-box):not(.tick-circle) { + border-color: transparent; +} +#JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item), +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item), +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) { + box-shadow: var(--JTCS-box-shadow-color) 0px 1px 3px; + background-color: var(--JTCS-tile-item-bg-color); +} +#JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button { + box-shadow: unset; + outline: unset; + background-color: unset; + color: var(--JTCS-text-color-on-bg); + transition: color 150ms ease-in-out; +} +#JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:hover, #JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:focus, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:hover, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:focus, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:hover, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button:focus { + box-shadow: unset; + outline: unset; + background-color: unset; + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active { + color: var(--JTCS-accent-color); + filter: drop-shadow(0px 0px 2px var(--JTCS-accent-color)); + transform: scale(1); + transition: transform 150ms ease-in-out; +} +#JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:hover, #JTCSSettingsApplication .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:focus, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:hover, +#slideshow-config .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:focus, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:hover, +#JTCS-custom-dialog .window-content li.tile-list-item:not(.new-tile-list-item) .actions .icon-button.active:focus { + transform: scale(1.12); +} +#JTCSSettingsApplication .window-content input[type=text], +#JTCSSettingsApplication .window-content select, +#slideshow-config .window-content input[type=text], +#slideshow-config .window-content select, +#JTCS-custom-dialog .window-content input[type=text], +#JTCS-custom-dialog .window-content select { + font-family: Arial, Helvetica, sans-serif; + border: 1px solid var(--JTCS-border-color); + background-color: var(--JTCS-input-background-color); +} +#JTCSSettingsApplication .window-content input[type=text] option, +#JTCSSettingsApplication .window-content select option, +#slideshow-config .window-content input[type=text] option, +#slideshow-config .window-content select option, +#JTCS-custom-dialog .window-content input[type=text] option, +#JTCS-custom-dialog .window-content select option { + background-color: var(--JTCS-input-background-color); +} +#JTCSSettingsApplication .window-content input[type=text] option:hover, #JTCSSettingsApplication .window-content input[type=text] option:focus, +#JTCSSettingsApplication .window-content select option:hover, +#JTCSSettingsApplication .window-content select option:focus, +#slideshow-config .window-content input[type=text] option:hover, +#slideshow-config .window-content input[type=text] option:focus, +#slideshow-config .window-content select option:hover, +#slideshow-config .window-content select option:focus, +#JTCS-custom-dialog .window-content input[type=text] option:hover, +#JTCS-custom-dialog .window-content input[type=text] option:focus, +#JTCS-custom-dialog .window-content select option:hover, +#JTCS-custom-dialog .window-content select option:focus { + background-color: var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); +} +#JTCSSettingsApplication .window-content input[type=text]:hover, +#JTCSSettingsApplication .window-content select:hover, +#slideshow-config .window-content input[type=text]:hover, +#slideshow-config .window-content select:hover, +#JTCS-custom-dialog .window-content input[type=text]:hover, +#JTCS-custom-dialog .window-content select:hover { + border-color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content .color-picker-group label, +#slideshow-config .window-content .color-picker-group label, +#JTCS-custom-dialog .window-content .color-picker-group label { + background-color: var(--JTCS-background-color); + padding-inline: 0.15rem; + border-radius: 3px; +} +#JTCSSettingsApplication .window-content input[data-responsive-color]#backgroundColor, +#slideshow-config .window-content input[data-responsive-color]#backgroundColor, +#JTCS-custom-dialog .window-content input[data-responsive-color]#backgroundColor { + border: 1px solid var(--JTCS-accent-color-20); +} +#JTCSSettingsApplication .window-content input[type=radio], +#JTCSSettingsApplication .window-content input[type=text], +#JTCSSettingsApplication .window-content select, +#slideshow-config .window-content input[type=radio], +#slideshow-config .window-content input[type=text], +#slideshow-config .window-content select, +#JTCS-custom-dialog .window-content input[type=radio], +#JTCS-custom-dialog .window-content input[type=text], +#JTCS-custom-dialog .window-content select { + color: var(--JTCS-text-color-on-bg); +} +#JTCSSettingsApplication .window-content [data-type=art], +#slideshow-config .window-content [data-type=art], +#JTCS-custom-dialog .window-content [data-type=art] { + --accent-color: var(--data-art-color); + transition: border-color 150ms ease-in; +} +#JTCSSettingsApplication .window-content [data-type=frame], +#slideshow-config .window-content [data-type=frame], +#JTCS-custom-dialog .window-content [data-type=frame] { + --accent-color: var(--data-frame-color); + transition: border-color 150ms ease-in; +} +#JTCSSettingsApplication .window-content [data-type=unlinked], +#slideshow-config .window-content [data-type=unlinked], +#JTCS-custom-dialog .window-content [data-type=unlinked] { + --accent-color: var(--data-unlinked-color); + transition: border-color 150ms ease-in; +} +#JTCSSettingsApplication .window-content [data-is-default], +#slideshow-config .window-content [data-is-default], +#JTCS-custom-dialog .window-content [data-is-default] { + --accent-color: var(--data-default-color); + border-color: var(--accent-color); + border: 2px solid var(--accent-color); + transition: border-color 150ms ease-in, border-style 150ms ease-in; +} +#JTCSSettingsApplication .window-content [data-is-default]:not(.accent), +#slideshow-config .window-content [data-is-default]:not(.accent), +#JTCS-custom-dialog .window-content [data-is-default]:not(.accent) { + border: 2px dotted var(--accent-color); + transition: border-color 150ms ease-out, border-style 150ms ease-in; +} +#JTCSSettingsApplication .window-content [data-is-default] .badge, +#slideshow-config .window-content [data-is-default] .badge, +#JTCS-custom-dialog .window-content [data-is-default] .badge { + background-color: var(--accent-color); + color: var(--JTCS-text-color-on-fill); +} +#JTCSSettingsApplication .window-content [data-is-default]:span, +#slideshow-config .window-content [data-is-default]:span, +#JTCS-custom-dialog .window-content [data-is-default]:span { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type]:hover, #JTCSSettingsApplication .window-content [data-type]:focus, #JTCSSettingsApplication .window-content [data-type]:focus-within, +#slideshow-config .window-content [data-type]:hover, +#slideshow-config .window-content [data-type]:focus, +#slideshow-config .window-content [data-type]:focus-within, +#JTCS-custom-dialog .window-content [data-type]:hover, +#JTCS-custom-dialog .window-content [data-type]:focus, +#JTCS-custom-dialog .window-content [data-type]:focus-within { + color: var(--accent-color); + transition: color 150ms ease-in, border-color 150 ease-in; +} +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > label.floating-label, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > label.floating-label, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > label.floating-label, +#slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > label.floating-label, +#slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > label.floating-label, +#slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > label.floating-label, +#JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > label.floating-label, +#JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > label.floating-label, +#JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > label.floating-label { + transition: color 150ms ease-in; + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > input, +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > select, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > input, +#JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > select, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > input, +#JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > select, +#slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > input, +#slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > select, +#slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > input, +#slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > select, +#slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > input, +#slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > select, +#JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > input, +#JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > select, +#JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > input, +#JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > select, +#JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > input, +#JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > select { + font-family: Arial, Helvetica, sans-serif; + border-color: var(--accent-color); + transition: border-color 150ms ease-out; +} +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > input ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > select ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > input ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > select ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > input ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > select ::-moz-placeholder, #slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > input ::-moz-placeholder, #slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > select ::-moz-placeholder, #slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > input ::-moz-placeholder, #slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > select ::-moz-placeholder, #slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > input ::-moz-placeholder, #slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > select ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > input ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > select ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > input ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > select ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > input ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > select ::-moz-placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > input :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > select :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > input :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > select :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > input :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > select :-ms-input-placeholder, #slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > input :-ms-input-placeholder, #slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > select :-ms-input-placeholder, #slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > input :-ms-input-placeholder, #slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > select :-ms-input-placeholder, #slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > input :-ms-input-placeholder, #slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > select :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > input :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > select :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > input :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > select :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > input :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > select :-ms-input-placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > input ::placeholder, +#JTCSSettingsApplication .window-content [data-type]:hover .form-group.has-floating-label > select ::placeholder, #JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > input ::placeholder, +#JTCSSettingsApplication .window-content [data-type]:focus .form-group.has-floating-label > select ::placeholder, #JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > input ::placeholder, +#JTCSSettingsApplication .window-content [data-type]:focus-within .form-group.has-floating-label > select ::placeholder, +#slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > input ::placeholder, +#slideshow-config .window-content [data-type]:hover .form-group.has-floating-label > select ::placeholder, +#slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > input ::placeholder, +#slideshow-config .window-content [data-type]:focus .form-group.has-floating-label > select ::placeholder, +#slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > input ::placeholder, +#slideshow-config .window-content [data-type]:focus-within .form-group.has-floating-label > select ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > input ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:hover .form-group.has-floating-label > select ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > input ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:focus .form-group.has-floating-label > select ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > input ::placeholder, +#JTCS-custom-dialog .window-content [data-type]:focus-within .form-group.has-floating-label > select ::placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type]:hover.border-accent, #JTCSSettingsApplication .window-content [data-type]:focus.border-accent, #JTCSSettingsApplication .window-content [data-type]:focus-within.border-accent, +#slideshow-config .window-content [data-type]:hover.border-accent, +#slideshow-config .window-content [data-type]:focus.border-accent, +#slideshow-config .window-content [data-type]:focus-within.border-accent, +#JTCS-custom-dialog .window-content [data-type]:hover.border-accent, +#JTCS-custom-dialog .window-content [data-type]:focus.border-accent, +#JTCS-custom-dialog .window-content [data-type]:focus-within.border-accent { + border-color: var(--accent-color); + transition: border-color 150ms ease-out; +} +#JTCSSettingsApplication .window-content [data-type].accent, +#slideshow-config .window-content [data-type].accent, +#JTCS-custom-dialog .window-content [data-type].accent { + color: var(--accent-color); + transition: color 150ms ease-in, border-color 150 ease-in; +} +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > label.floating-label, +#slideshow-config .window-content [data-type].accent .form-group.has-floating-label > label.floating-label, +#JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > label.floating-label { + transition: color 150ms ease-in; + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > input, +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > select, +#slideshow-config .window-content [data-type].accent .form-group.has-floating-label > input, +#slideshow-config .window-content [data-type].accent .form-group.has-floating-label > select, +#JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > input, +#JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > select { + font-family: Arial, Helvetica, sans-serif; + border-color: var(--accent-color); + transition: border-color 150ms ease-out; +} +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > input ::-moz-placeholder, #JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > select ::-moz-placeholder, #slideshow-config .window-content [data-type].accent .form-group.has-floating-label > input ::-moz-placeholder, #slideshow-config .window-content [data-type].accent .form-group.has-floating-label > select ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > input ::-moz-placeholder, #JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > select ::-moz-placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > input :-ms-input-placeholder, #JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > select :-ms-input-placeholder, #slideshow-config .window-content [data-type].accent .form-group.has-floating-label > input :-ms-input-placeholder, #slideshow-config .window-content [data-type].accent .form-group.has-floating-label > select :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > input :-ms-input-placeholder, #JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > select :-ms-input-placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > input ::placeholder, +#JTCSSettingsApplication .window-content [data-type].accent .form-group.has-floating-label > select ::placeholder, +#slideshow-config .window-content [data-type].accent .form-group.has-floating-label > input ::placeholder, +#slideshow-config .window-content [data-type].accent .form-group.has-floating-label > select ::placeholder, +#JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > input ::placeholder, +#JTCS-custom-dialog .window-content [data-type].accent .form-group.has-floating-label > select ::placeholder { + color: var(--accent-color); +} +#JTCSSettingsApplication .window-content [data-type].accent.border-accent, +#slideshow-config .window-content [data-type].accent.border-accent, +#JTCS-custom-dialog .window-content [data-type].accent.border-accent { + border-color: var(--accent-color); + transition: border-color 150ms ease-out; +} +#JTCSSettingsApplication .window-content [data-type].accent [data-type=unlinked], +#slideshow-config .window-content [data-type].accent [data-type=unlinked], +#JTCS-custom-dialog .window-content [data-type].accent [data-type=unlinked] { + --accent-color: var(--data-unlinked-color); +} +#JTCSSettingsApplication .window-content [data-type].accent [data-type=unlinked] + label, +#slideshow-config .window-content [data-type].accent [data-type=unlinked] + label, +#JTCS-custom-dialog .window-content [data-type].accent [data-type=unlinked] + label { + --accent-color: var(--data-unlinked-color); +} +#JTCSSettingsApplication .primary, +#slideshow-config .primary, +#JTCS-custom-dialog .primary { + background-color: var(--JTCS-accent-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + position: relative; + border: 2px solid transparent; + transition: border-color 150ms ease-in; +} +#JTCSSettingsApplication .primary:hover, #JTCSSettingsApplication .primary:focus, +#slideshow-config .primary:hover, +#slideshow-config .primary:focus, +#JTCS-custom-dialog .primary:hover, +#JTCS-custom-dialog .primary:focus { + border: 2px solid var(--JTCS-accent-color-80); + transition: border-color 150ms ease-out; +} +#JTCSSettingsApplication button.file-picker, #JTCSSettingsApplication button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button), +#slideshow-config button.file-picker, +#slideshow-config button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button), +#JTCS-custom-dialog button.file-picker, +#JTCS-custom-dialog button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button) { + background-color: var(--JTCS-accent-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +#JTCSSettingsApplication button.file-picker:hover, #JTCSSettingsApplication button.file-picker:focus, #JTCSSettingsApplication button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):hover, #JTCSSettingsApplication button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):focus, +#slideshow-config button.file-picker:hover, +#slideshow-config button.file-picker:focus, +#slideshow-config button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):hover, +#slideshow-config button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):focus, +#JTCS-custom-dialog button.file-picker:hover, +#JTCS-custom-dialog button.file-picker:focus, +#JTCS-custom-dialog button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):hover, +#JTCS-custom-dialog button[type=button]:not(.danger):not([data-action=scrollTo]):not(.new-tile-button):not(.secondary):not([data-button=danger]):not([data-button=reset]):not(.icon-button):not(.instructions__button):focus { + transform: scale(1.12); +} +#JTCSSettingsApplication .secondary, +#slideshow-config .secondary, +#JTCS-custom-dialog .secondary { + background-color: transparent; + color: var(--JTCS-accent-color); + border: 2px solid var(--JTCS-accent-color); + transition: background-color 120ms ease-in, color 120ms ease-in; +} +#JTCSSettingsApplication .secondary:hover, #JTCSSettingsApplication .secondary:focus, #JTCSSettingsApplication .secondary.active, +#slideshow-config .secondary:hover, +#slideshow-config .secondary:focus, +#slideshow-config .secondary.active, +#JTCS-custom-dialog .secondary:hover, +#JTCS-custom-dialog .secondary:focus, +#JTCS-custom-dialog .secondary.active { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); +} +#JTCSSettingsApplication .primary, +#JTCSSettingsApplication .secondary, +#slideshow-config .primary, +#slideshow-config .secondary, +#JTCS-custom-dialog .primary, +#JTCS-custom-dialog .secondary { + max-width: 300px; +} + +#JTCSSettingsApplication { + height: clamp(50vh, 530px, 80vh); + max-width: 60vw; +} +#JTCSSettingsApplication header, +#JTCSSettingsApplication footer { + position: relative; +} +#JTCSSettingsApplication header:after, +#JTCSSettingsApplication footer:after { + position: absolute; + pointer-events: none; + content: ""; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: 0px 2px 8px var(--JTCS-box-shadow-color); +} +#JTCSSettingsApplication footer:after { + box-shadow: 0px -2px 8px var(--JTCS-box-shadow-color); +} +#JTCSSettingsApplication .color-picker-group { + position: relative; +} +#JTCSSettingsApplication .color-picker-group .picker_wrapper { + position: absolute; + /* top: -50%; */ + z-index: 800; + transform: translateY(-50%); + left: 100%; +} +#JTCSSettingsApplication .window-content { + padding: unset; +} +#JTCSSettingsApplication .window-content form { + display: flex; + flex-direction: column; +} +#JTCSSettingsApplication .window-content form .form-content { + flex: 1 1 0; +} +#JTCSSettingsApplication .window-content form .form-content > .form-group, +#JTCSSettingsApplication .window-content form .form-content > .form-group-stacked { + border-radius: 8px; + background-color: var(--JTCS-elevation-BG-color); + box-shadow: 1px 0px 8px 2px var(--JTCS-box-shadow-color); + padding: 1rem; +} +#JTCSSettingsApplication .window-content form .form-content > .form-group img.demo-img, +#JTCSSettingsApplication .window-content form .form-content > .form-group-stacked img.demo-img { + min-width: 200px; + width: 60%; + -o-object-fit: contain; + object-fit: contain; + margin-left: auto; + margin-right: auto; + margin-block: 1rem; +} +#JTCSSettingsApplication .window-content form .form-content > .form-group a, +#JTCSSettingsApplication .window-content form .form-content > .form-group-stacked a { + display: inline-block; +} +#JTCSSettingsApplication .window-content form .form-content > .form-group select, +#JTCSSettingsApplication .window-content form .form-content > .form-group-stacked select { + min-width: 200px; +} +#JTCSSettingsApplication .window-content form .form-content > .form-group fieldset, +#JTCSSettingsApplication .window-content form .form-content > .form-group-stacked fieldset { + border: unset; +} +#JTCSSettingsApplication .window-content form input[type=range]::-webkit-slider-thumb { + background-color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content form .range-value { + color: var(--JTCS-accent-color); + font-weight: bold; +} +#JTCSSettingsApplication .window-content form p.notes { + font-weight: 500; + margin-bottom: unset; +} +#JTCSSettingsApplication .window-content form p.notes span.accent { + --accent-color: var(--JTCS-accent-color); + color: var(--JTCS-accent-color); +} +#JTCSSettingsApplication .window-content form .sheet-footer { + flex: 0; +} + +#sheet-controls { + z-index: 100; + position: absolute; + min-width: 65px; +} +#sheet-controls[data-position=bottom-right] { + bottom: 5px; + right: 30px; + flex-direction: row-reverse; +} +#sheet-controls[data-position=top-left] { + top: 5px; + left: 5px; +} +#sheet-controls[data-position=bottom-left] { + bottom: 5px; + left: 5px; +} +#sheet-controls[data-position=top-right] { + bottom: 5px; + left: 5px; +} +#sheet-controls button { + width: 36px; + height: 36px; + --color-shadow-primary: var(--JTCS-accent-color); + background-color: var(--JTCS-accent-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +#sheet-controls button:hover, #sheet-controls button:focus { + transform: scale(1.12); +} +#sheet-controls button.active { + background-color: var(--JTCS-accent-color); + box-shadow: 0px 0px 8px 2px var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +#sheet-controls button.active:hover, #sheet-controls button.active:focus { + transform: scale(1.12); +} +#slideshow-config { + width: min(600px, 45vw); + max-width: min(600px, 55vw); +} +#slideshow-config .window-content { + --flow-space: 0.5rem; + color: var(--color-text-primary); + scrollbar-color: var(--JTCS-accent-color) var(--JTCS-accent-color-70); +} +#slideshow-config .window-content::-webkit-scrollbar-track { + background-color: var(--JTCS-accent-color-70); +} +#slideshow-config .window-content::-webkit-scrollbar-thumb { + background-color: var(--JTCS-accent-color); + border-color: var(--JTCS-accent-color-50); +} +#slideshow-config .window-content header .wrapper { + flex-wrap: wrap; +} +#slideshow-config .window-content header .wrapper .form-group { + flex: 2; +} +#slideshow-config .window-content header .wrapper .actions { + flex: 1; + min-width: 66px; + max-width: 108px; +} +#slideshow-config .window-content #JTCS-config-instructions { + position: relative; + margin-top: unset; + min-width: 200px; + max-width: 250px; + font-size: var(--font-size-12); + background-color: transparent; + flex-shrink: 0; + position: absolute; + top: 20%; + left: 100%; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content { + border-radius: 5px; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + background-color: transparent; + width: 100%; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content p, +#slideshow-config .window-content #JTCS-config-instructions .instructions__content ul, +#slideshow-config .window-content #JTCS-config-instructions .instructions__content ol, +#slideshow-config .window-content #JTCS-config-instructions .instructions__content li { + color: var(--JTCS-text-color-on-bg); +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content .art-color { + color: var(--data-art-color); + font-weight: bold; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content .frame-color { + color: var(--data-frame-color); + font-weight: bold; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content .unlinked-color { + color: var(--data-unlinked-color); + font-weight: bold; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content .default-color { + color: var(--data-default-color); + font-weight: bold; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; + border-top: 2px solid var(--JTCS-box-shadow-color); + background-color: var(--JTCS-background-color-00); + opacity: 95%; + -webkit-backdrop-filter: blur(12px); + backdrop-filter: blur(12px); + border-radius: 5px; + border-top-left-radius: 0px; + border-left: 0.25rem solid var(--JTCS-accent-color); + border-bottom-left-radius: 0px; +} +#slideshow-config .window-content #JTCS-config-instructions .instructions__content:not(.JTCS-hidden) { + padding: 1rem; + min-height: 10em; +} +#slideshow-config .window-content #JTCS-config-instructions button { + background-color: var(--JTCS-background-color); + border: 2px solid var(--JTCS-accent-color); + color: var(--JTCS-accent-color); + position: absolute; + bottom: 100%; + left: 0; + max-width: 2rem; + max-height: 2rem; +} +#slideshow-config .window-content #JTCS-config-instructions button.active { + background-color: var(--JTCS-accent-color); + box-shadow: 0px 0px 8px 2px var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +#slideshow-config .window-content #JTCS-config-instructions button.active:hover, #slideshow-config .window-content #JTCS-config-instructions button.active:focus { + transform: scale(1.12); +} +#slideshow-config .window-content form { + height: 100%; +} +#slideshow-config .window-content .tilesInScene { + height: 95%; + position: relative; +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles h3 { + background-color: var(--data-art-color); + width: 100%; + color: var(--JTCS-text-color-on-fill); + border-top-left-radius: 8px; + border-top-right-radius: 8px; + white-space: nowrap; + text-align: center; + margin-bottom: unset; +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .item-wrapper { + border-color: var(--data-art-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .item-wrapper ul { + scrollbar-color: var(--data-art-color) var(--data-art-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .item-wrapper ul::-webkit-scrollbar-track { + background-color: var(--JTCS-background-color-30); +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .item-wrapper ul::-webkit-scrollbar-thumb { + background-color: var(--data-art-color); + border-color: var(--data-art-color); + box-shadow: 0px 0px 4px var(--data-art-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button { + border-radius: unset; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + background-color: transparent; + color: var(--data-art-color); + border-top: 2px solid var(--data-art-color); + transition: background-color 120ms ease-in, color 120ms ease-in; + border-width: 1px; +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button:hover, #slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button:focus, #slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button.active { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: var(--data-art-color); + color: var(--JTCS-text-color-on-fill); +} +#slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button:hover, #slideshow-config .window-content .tilesInScene .wrapper.art-tiles .new-tile-list-item button:focus { + box-shadow: unset; +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles h3 { + background-color: var(--data-frame-color); + width: 100%; + color: var(--JTCS-text-color-on-fill); + border-top-left-radius: 8px; + border-top-right-radius: 8px; + white-space: nowrap; + text-align: center; + margin-bottom: unset; +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .item-wrapper { + border-color: var(--data-frame-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .item-wrapper ul { + scrollbar-color: var(--data-frame-color) var(--data-frame-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .item-wrapper ul::-webkit-scrollbar-track { + background-color: var(--JTCS-background-color-30); +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .item-wrapper ul::-webkit-scrollbar-thumb { + background-color: var(--data-frame-color); + border-color: var(--data-frame-color); + box-shadow: 0px 0px 4px var(--data-frame-color); +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button { + border-radius: unset; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + background-color: transparent; + color: var(--data-frame-color); + border-top: 2px solid var(--data-frame-color); + transition: background-color 120ms ease-in, color 120ms ease-in; + border-width: 1px; +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button:hover, #slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button:focus, #slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button.active { + transition: background-color 120ms ease-out, color 120ms ease-out; + background-color: var(--data-frame-color); + color: var(--JTCS-text-color-on-fill); +} +#slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button:hover, #slideshow-config .window-content .tilesInScene .wrapper.frame-tiles .new-tile-list-item button:focus { + box-shadow: unset; +} +#slideshow-config .window-content .tilesInScene .wrapper h3 { + font-size: min(1.25em, 100%); +} +#slideshow-config .window-content .tilesInScene .wrapper { + overflow: hidden; +} +#slideshow-config .window-content .tilesInScene .wrapper .item-wrapper { + overflow: hidden; +} +#slideshow-config .window-content .tilesInScene .item-wrapper { + display: flex; + flex-direction: column; +} +#slideshow-config .window-content .tilesInScene .item-wrapper ul { + flex: 1 0 0; +} +#slideshow-config .window-content .tilesInScene .item-wrapper .new-tile-list-item { + flex: 0; + height: 100%; +} +#slideshow-config .window-content .tilesInScene .item-wrapper .new-tile-list-item button { + height: 100%; +} +#slideshow-config .window-content .tile-list-item:focus-within { + outline: 2px solid var(--accent-color); +} +#slideshow-config .window-content .tile-list-item select { + height: var(--form-field-height); +} +#slideshow-config .window-content li { + --flow-space: 0.25em; +} +#slideshow-config .window-content li .actions { + display: flex; + align-items: center; + flex: 0 0 50px; + justify-content: center; +} +#slideshow-config .window-content h1, +#slideshow-config .window-content h2, +#slideshow-config .window-content h3 { + font-weight: bold; +} + +.clickableImageContainer { + display: inline-flex; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + align-items: center; + width: fit-content; + position: relative; +} +.clickableImageContainer .clickableImageControls { + opacity: 0%; + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 400; + left: 5px; + display: flex; + align-items: flex-start; + height: -webkit-fit-content; + height: -moz-fit-content; + height: fit-content; + transition: opacity 100ms ease-in; + pointer-events: none; +} +.clickableImageContainer .clickableImageControls:hover { + opacity: 100%; +} +.clickableImageContainer .displayLocations * { + pointer-events: auto; +} +.clickableImageContainer .displayTiles { + pointer-events: auto; + display: flex; + flex-wrap: wrap; + display: flex; + flex-direction: row; + border: none; + color: white; + padding: 0rem 0rem; + min-width: 50%; +} +.clickableImageContainer .displayTiles button { + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + white-space: nowrap; + border: unset; + box-shadow: var(--box-shadow); + background-color: var(--data-art-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +.clickableImageContainer .displayTiles button:hover, .clickableImageContainer .displayTiles button:focus { + transform: scale(1.12); +} +.clickableImageContainer .displayTiles button[data-is-default] { + background-color: var(--data-default-color); + box-shadow: unset; + color: var(--JTCS-text-color-on-fill); + border: unset; + transform: scale(1); + transition: transform 120ms ease-in-out; + transition: transform 120ms ease-in-out; +} +.clickableImageContainer .displayTiles button[data-is-default]:hover, .clickableImageContainer .displayTiles button[data-is-default]:focus { + transform: scale(1.12); +} +.clickableImageContainer .displayTiles * { + pointer-events: auto; +} +.clickableImageContainer .displayTiles legend { + background-color: inherit; + padding: 0.5rem; +} +.clickableImageContainer .displayTiles label:first-of-type { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; +} +.clickableImageContainer .displayTiles label:last-of-type { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; +} + +.sheet.actor .clickableImageContainer, +.sheet.item .clickableImageContainer { + padding: unset; + margin: unset; + max-width: -webkit-fit-content; + max-width: -moz-fit-content; + max-width: fit-content; + max-height: -webkit-fit-content; + max-height: -moz-fit-content; + max-height: fit-content; +} +.sheet.actor .clickableImageContainer .clickableImageControls, +.sheet.item .clickableImageContainer .clickableImageControls { + top: 0%; + transform: translateY(0%); +} +.sheet.actor .clickableImageContainer .clickableImageControls button.floating-control, +.sheet.item .clickableImageContainer .clickableImageControls button.floating-control { + background: var(--bg-color); + border: unset; +} + +.clickableImage, +.rightClickableImage { + border: 0px solid transparent; + box-shadow: inset 0px 0px 2px transparent; + outline: 2px dotted transparent; + transition: all 100ms ease-out; + flex: 1; +} +.clickableImage:hover, +.rightClickableImage:hover { + border: 2px solid var(--JTCS-accent-color); + box-shadow: inset 2px 2px 8px rgba(50, 51, 59, 0.5); + cursor: pointer; + transition: all 100ms ease-out; +} +.clickableImage:hover ~ .clickableImageControls, +.rightClickableImage:hover ~ .clickableImageControls { + opacity: 100%; +} +.clickableImage:active, +.rightClickableImage:active { + box-shadow: inset 4px 4px 16px rgba(50, 51, 59, 0.5); +} + +.error::before { + content: "⚠"; + color: var(--JTCS-danger-color); +} + +[data-missing=true] { + color: var(--JTCS-danger-color); +} +[data-missing=true]:hover { + color: var(--JTCS-warning-color); +} +[data-missing=true] button:disabled { + opacity: 50%; +} +[data-missing=true] button:disabled:hover { + cursor: not-allowed; +} + +.unlinked-tiles-list { + padding-block: 2em; +} +.unlinked-tiles-list h3 { + color: var(--accent-color); +} +.unlinked-tiles-list ul { + background-color: var(--JTCS-elevation-BG-color); +} +.unlinked-tiles-list li { + margin: unset; + list-style-type: none; + padding: unset; +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] { + display: flex; + height: 100%; + width: 100%; + align-items: center; +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] { + margin-right: 0.5rem; + padding: 1rem; +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label { + display: flex; + gap: 1rem; + color: var(--JTCS-text-color-on-bg); + border: unset; + border-bottom: 1px solid var(--JTCS-accent-color); + --border-color: var(--data-unlinked-color); + transition: color 150ms ease-in, background-color 150ms ease-in, border-color 150ms ease-in; +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label .tick-box, +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label .tick-circle { + border-color: var(--JTCS-accent-color); +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label:hover { + background-color: var(--data-unlinked-color); + color: var(--JTCS-text-color-on-fill); + border-color: var(--data-unlinked-color); +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label:hover .tick-box, +.unlinked-tiles-list li .input-span[data-variant=visible-tick] input[type=radio] + label:hover .tick-circle { + transition: border-color 150ms ease-in; + border-color: var(--data-unlinked-color); +} +.unlinked-tiles-list li .input-span[data-variant=visible-tick] label { + padding: 1rem; + height: 100%; + width: 100%; +}/*# sourceMappingURL=styles.css.map */ \ No newline at end of file diff --git a/styles/styles.css.map b/styles/styles.css.map new file mode 100644 index 0000000..2913724 --- /dev/null +++ b/styles/styles.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["styles.css","_variables.scss","_mixins.scss","_animations.scss","_utility.scss","styles.scss"],"names":[],"mappings":"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","file":"styles.css"} \ No newline at end of file diff --git a/styles/styles.scss b/styles/styles.scss new file mode 100644 index 0000000..2c7dc20 --- /dev/null +++ b/styles/styles.scss @@ -0,0 +1,917 @@ +@use "colors" as *; +@use "utility" as *; +@use "mixins" as *; +@import "_variables"; +:root { + --box-shadow-inset: 1px 1px 3px 1px rgba(0, 0, 0, 0.36) inset; + --box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.36); +} +$tile-types: ("frame", "art", "unlinked", "default"); +#slideshow-config, +.clickableImageContainer { + .empty-state { + @extend .padding-small; + background-color: var(--JTCS-elevation-BG-color); + color: var(--JTCS-text-color-on-bg); + text-align: center; + span.accent { + padding-inline: 0.15rem; + font-weight: bold; + } + &[data-type="art"] { + span.accent { + color: var(--data-art-color); + } + } + &[data-type="frame"] { + span.accent { + color: var(--data-frame-color); + } + } + } +} + +#JTCSSettingsApplication, +#slideshow-config, +#JTCS-custom-dialog { + button > i { + margin-right: unset; + } + .window-content { + --color-shadow-primary: var(--JTCS-accent-color); + background: var(--JTCS-background-color); + .container { + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + border: 1px solid var(--JTCS-background-color-30); + } + + --flow-space: 2em; + .flow { + @extend .flow; + } + .elevation-1 { + background: var(--JTCS-background-color); + } + .form-group, + .form-group-stacked, + .input-span { + button { + &.danger, + &.theme { + width: 100%; + max-width: 300px; + margin-inline: auto; + // margin-right: auto; + } + } + &.has-floating-label { + input { + width: 100%; + max-width: 300px; + } + label { + color: var(--JTCS-text-color-on-bg); + } + } + .file-picker-group { + align-items: stretch; + input { + flex: 1; + width: 100%; + max-width: 450px; + border: 1px solid var(--JTCS-accent-color-20); + } + button { + flex: 0; + } + } + &.color-picker-container { + input { + width: 100%; + max-width: 300px; + } + align-items: stretch; + .color-picker-group { + flex: 0; + min-width: fit-content; + } + .color-actions { + flex: 0; + } + } + } + + .form-group-stacked { + --flow-space: 1em; + p.notes { + color: var(--JTCS-text-color-on-bg); + } + } + select, + input, + label, + button { + cursor: pointer; + } + + $selectors: ( + "p:not(.inline-notification)", + ".file-picker-group > label", + ".color-picker-group > label", + "span:not(.input-span) label", + "li:not(.accent) label", + ".icon-button:not(.danger):not(.danger-text)" + ); + $selectors: join($selectors, returnAllHeadings()); + // $headers: returnAllHeadings(); + $properties: ( + color: var(--JTCS-text-color-on-bg), + ); + @include printSelectors($selectorList: $selectors, $propertyMap: $properties); + + // remove all border-bottoms on headings + + @include printSelectors( + $selectorList: returnAllHeadings(), + $propertyMap: ( + border-bottom: unset, + color: var(--JTCS-accent-color), + ) + ); + + // have labels highlight with accent color + .form-group, + .color-picker-group, + .file-picker-group, + .input-span { + &.has-floating-label { + input { + height: 100%; + } + label { + white-space: nowrap; + } + &:hover { + --accent-color: var(--JTCS-accent-color); + label { + white-space: nowrap; + color: var(--accent-color); + } + } + &:focus { + outline: 1px solid var(--JTCS-accent-color); + } + } + } + .form-group, + .form-group-stacked { + .input-span { + flex: 1; + width: fit-content; + max-width: fit-content; + } + } + // remove borders from spans and list items + span:not(.input-span):not(.tick-box):not(.tick-circle) { + border-color: transparent; + } + + li.tile-list-item:not(.new-tile-list-item) { + box-shadow: var(--JTCS-box-shadow-color) 0px 1px 3px; + background-color: var(--JTCS-tile-item-bg-color); + .actions .icon-button { + box-shadow: unset; + outline: unset; + background-color: unset; + color: var(--JTCS-text-color-on-bg); + transition: color 150ms ease-in-out; + &:hover, + &:focus { + box-shadow: unset; + outline: unset; + background-color: unset; + color: var(--JTCS-accent-color); + } + &.active { + color: var(--JTCS-accent-color); + filter: drop-shadow(0px 0px 2px var(--JTCS-accent-color)); + transform: scale(1); + transition: transform 150ms ease-in-out; + &:hover, + &:focus { + transform: scale(1.12); + } + } + } + // box-shadow: var(--JTCS-background-color-40) 0px 0px 0px 2px, + // var(--JTCS-background-color-60) 0px 4px 6px -1px, var(--JTCS-background-color-10) 0px 1px 0px inset; + } + + input[type="text"], + select { + font-family: Arial, Helvetica, sans-serif; + border: 1px solid var(--JTCS-border-color); + background-color: var(--JTCS-input-background-color); + option { + background-color: var(--JTCS-input-background-color); + &:hover, + &:focus { + background-color: var(--JTCS-accent-color); + color: var(--JTCS-text-color-on-fill); + } + } + &:hover { + border-color: var(--JTCS-accent-color); + } + } + .color-picker-group { + label { + background-color: var(--JTCS-background-color); + padding-inline: 0.15rem; + border-radius: 3px; + } + } + input[data-responsive-color]#backgroundColor { + border: 1px solid var(--JTCS-accent-color-20); + } + + //for neutral text + input[type="radio"], + input[type="text"], + select { + color: var(--JTCS-text-color-on-bg); + } + // for the list items in the slideshow config + [data-type="art"] { + --accent-color: var(--data-art-color); + // border-color: var(--accent-color); + transition: border-color 150ms ease-in; + } + + [data-type="frame"] { + --accent-color: var(--data-frame-color); + // border-color: var(--accent-color); + transition: border-color 150ms ease-in; + } + [data-type="unlinked"] { + --accent-color: var(--data-unlinked-color); + // border-color: var(--accent-color); + transition: border-color 150ms ease-in; + } + [data-is-default] { + --accent-color: var(--data-default-color); + border-color: var(--accent-color); + border: 2px solid var(--accent-color); + transition: border-color 150ms ease-in, border-style 150ms ease-in; + &:not(.accent) { + // outline: 1px solid var(--accent-color); + border: 2px dotted var(--accent-color); + transition: border-color 150ms ease-out, border-style 150ms ease-in; + } + .badge { + background-color: var(--accent-color); + color: var(--JTCS-text-color-on-fill); + } + &:span { + color: var(--accent-color); + } + } + [data-type] { + &:hover, + &:focus, + &:focus-within { + color: var(--accent-color); + transition: color 150ms ease-in, border-color 150 ease-in; + .form-group.has-floating-label { + > label.floating-label { + transition: color 150ms ease-in; + color: var(--accent-color); + } + > input, + > select { + font-family: Arial, Helvetica, sans-serif; + border-color: var(--accent-color); + transition: border-color 150ms ease-out; + ::placeholder { + color: var(--accent-color); + } + } + } + &.border-accent { + border-color: var(--accent-color); + transition: border-color 150ms ease-out; + } + } + } + + //for all things with a "data-type" (referring to tile), and an accent class on them + [data-type].accent { + color: var(--accent-color); + transition: color 150ms ease-in, border-color 150 ease-in; + + .form-group.has-floating-label { + > label.floating-label { + transition: color 150ms ease-in; + color: var(--accent-color); + } + > input, + > select { + // border: unset; + // border-bottom: 2px solid; + + font-family: Arial, Helvetica, sans-serif; + border-color: var(--accent-color); + transition: border-color 150ms ease-out; + ::placeholder { + color: var(--accent-color); + } + } + } + &.border-accent { + border-color: var(--accent-color); + transition: border-color 150ms ease-out; + } + [data-type="unlinked"] { + --accent-color: var(--data-unlinked-color); + + label { + --accent-color: var(--data-unlinked-color); + } + } + } + } + .primary { + @include JTCS-accent-fill($hoverStyle: "border"); + } + + button { + &.file-picker, + &[type="button"]:not(.danger):not([data-action="scrollTo"]):not(.new-tile-button):not(.secondary):not([data-button="danger"]):not([data-button="reset"]):not(.icon-button):not(.instructions__button) { + @include JTCS-accent-fill(); + } + } + .secondary { + @include JTCS-accent-ghost(); + } + .primary, + .secondary { + max-width: 300px; + } +} + +#JTCSSettingsApplication { + // min-height: 50vh; + height: clamp(50vh, 530px, 80vh); + max-width: 60vw; + header, + footer { + position: relative; + &:after { + position: absolute; + pointer-events: none; + content: ""; + z-index: 1; + top: 0; + left: 0; + width: 100%; + height: 100%; + box-shadow: 0px 2px 8px var(--JTCS-box-shadow-color); + } + } + footer { + &:after { + box-shadow: 0px -2px 8px var(--JTCS-box-shadow-color); + } + } + .color-picker-group { + position: relative; + .picker_wrapper { + position: absolute; + /* top: -50%; */ + z-index: 800; + transform: translateY(-50%); + left: 100%; + } + } + .window-content { + padding: unset; + // overflow: hidden; + form { + display: flex; + flex-direction: column; + .form-content { + flex: 1 1 0; + > .form-group, + > .form-group-stacked { + img.demo-img { + min-width: 200px; + width: 60%; + object-fit: contain; + margin-left: auto; + margin-right: auto; + margin-block: 1rem; + } + a { + display: inline-block; + } + select { + min-width: 200px; + } + fieldset { + border: unset; + } + border-radius: 8px; + background-color: var(--JTCS-elevation-BG-color); + box-shadow: 1px 0px 8px 2px var(--JTCS-box-shadow-color); + padding: 1rem; + } + } + input[type="range"]::-webkit-slider-thumb { + background-color: var(--JTCS-accent-color); + } + .range-value { + color: var(--JTCS-accent-color); + font-weight: bold; + } + p.notes { + font-weight: 500; + margin-bottom: unset; + span.accent { + --accent-color: var(--JTCS-accent-color); + color: var(--JTCS-accent-color); + } + } + .sheet-footer { + flex: 0; + } + } + } +} + +#sheet-controls { + z-index: 100; + position: absolute; + &[data-position="bottom-right"] { + bottom: 5px; + right: 30px; + flex-direction: row-reverse; + } + &[data-position="top-left"] { + top: 5px; + left: 5px; + } + &[data-position="bottom-left"] { + bottom: 5px; + left: 5px; + } + &[data-position="top-right"] { + bottom: 5px; + left: 5px; + } + + // transform: translateX(-50%); + min-width: 65px; + button { + width: 36px; + height: 36px; + --color-shadow-primary: var(--JTCS-accent-color); + @include JTCS-accent-fill( + $include-shadow: false, + $is-icon-button: true, + $primary-color: var(--JTCS-accent-color) + ); + // background-color: var(--JTCS-accent-color); + &.active { + @include JTCS-accent-fill($include-shadow: true, $is-icon-button: true); + } + &.JTCS-hidden { + @extend .JTCS-hidden; + } + } +} + +#slideshow-config { + width: min(600px, 45vw); + max-width: min(600px, 55vw); + + .window-content { + --flow-space: 0.5rem; + color: var(--color-text-primary); + scrollbar-color: var(--JTCS-accent-color) var(--JTCS-accent-color-70); + &::-webkit-scrollbar-track { + background-color: var(--JTCS-accent-color-70); + } + &::-webkit-scrollbar-thumb { + background-color: var(--JTCS-accent-color); + border-color: var(--JTCS-accent-color-50); + } + + // max-height: calc(100vh - 200px); + // width: min(600px, 45vw); + // max-width: min(600px, 45vw); + header .wrapper { + flex-wrap: wrap; + + .form-group { + flex: 2; + } + .actions { + flex: 1; + min-width: 66px; + max-width: 108px; + } + } + #JTCS-config-instructions { + position: relative; + margin-top: unset; + + min-width: 200px; + max-width: 250px; + font-size: var(--font-size-12); + // width: clamp(200px, 40ch, 250px); + + background-color: transparent; + flex-shrink: 0; + position: absolute; + top: 20%; + left: calc(100%); + + .instructions__content { + border-radius: 5px; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + background-color: transparent; + width: 100%; + p, + ul, + ol, + li { + color: var(--JTCS-text-color-on-bg); + } + $list: ("art", "frame", "unlinked", "default"); + @each $var in $list { + .#{$var}-color { + color: var(--data-#{$var}-color); + font-weight: bold; + } + } + + &:before { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: -1; + border-top: 2px solid var(--JTCS-box-shadow-color); + background-color: var(--JTCS-background-color-00); + opacity: 95%; + backdrop-filter: blur(12px); + border-radius: 5px; + border-top-left-radius: 0px; + border-left: 0.25rem solid var(--JTCS-accent-color); + border-bottom-left-radius: 0px; + // filter: blur(3px); + } + &:not(.JTCS-hidden) { + padding: 1rem; + min-height: 10em; + // max-height + } + // opacity: 0%; + } + button { + background-color: var(--JTCS-background-color); + border: 2px solid var(--JTCS-accent-color); + color: var(--JTCS-accent-color); + &.active { + @include JTCS-accent-fill( + $include-shadow: true, + $is-icon-button: true + ); + } + + // @include JTCS-accent-fill($include-shadow: true); + position: absolute; + // top: 0; + bottom: 100%; + left: 0; + // right: 100%; + max-width: 2rem; + max-height: 2rem; + } + } + form { + height: 100%; + // max-height: 80vh; + } + .tilesInScene { + .wrapper { + $tile-types: ( + art: "art-tiles", + frame: "frame-tiles", + ); + $tile-type-colors: (); + @each $type, $selector in $tile-types { + &.#{$selector} { + h3 { + background-color: var(--data-#{$type}-color); + width: 100%; + color: var(--JTCS-text-color-on-fill); + border-top-left-radius: 8px; + border-top-right-radius: 8px; + white-space: nowrap; + text-align: center; + margin-bottom: unset; + } + .item-wrapper { + border-color: var(--data-#{$type}-color); + ul { + scrollbar-color: var(--data-#{$type}-color) + var(--data-#{$type}-color); + &::-webkit-scrollbar-track { + background-color: var(--JTCS-background-color-30); + } + &::-webkit-scrollbar-thumb { + background-color: var(--data-#{$type}-color); + border-color: var(--data-#{$type}-color); + box-shadow: 0px 0px 4px var(--data-#{$type}-color); + } + } + } + .new-tile-list-item { + button { + border-radius: unset; + border-bottom-left-radius: 5px; + border-bottom-right-radius: 5px; + @include JTCS-accent-ghost( + $primary-color: var(--data-#{$type}-color), + $border-side: "-top" + ); + border-width: 1px; + &:hover, + &:focus { + box-shadow: unset; + } + } + } + } + } + h3 { + font-size: min(1.25em, 100%); + } + } + + height: 95%; + position: relative; + .wrapper { + overflow: hidden; + .item-wrapper { + overflow: hidden; + } + } + + .item-wrapper { + display: flex; + flex-direction: column; + // background-color: var(--JTCS-background-color-10); + // box-shadow: 1px 0px 8px 2px var(--JTCS-box-shadow-color); + // max-height: 90%; + ul { + flex: 1 0 0; + // max-height: 90%; + } + .new-tile-list-item { + flex: 0; + height: 100%; + button { + height: 100%; + } + } + } + } + + .tile-list-item { + &:focus-within { + outline: 2px solid var(--accent-color); + } + select { + height: var(--form-field-height); + } + // &[data-is-default] { + // border-width: 5px; + // border-color: hotpink !important; + // } + } + li { + --flow-space: 0.25em; + } + li .actions { + display: flex; + align-items: center; + flex: 0 0 50px; + justify-content: center; + } + h1, + h2, + h3 { + font-weight: bold; + } + } + // --color-text-primary: ; +} + +.clickableImageContainer { + display: inline-flex; + @extend .padding-medium; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + align-items: center; + width: fit-content; + @extend .reset-margin; + + position: relative; + + .clickableImageControls { + opacity: 0%; + position: absolute; + top: 50%; + transform: translateY(-50%); + z-index: 400; + left: 5px; + display: flex; + align-items: flex-start; + // width: 100%; + height: fit-content; + + transition: opacity 100ms ease-in; + pointer-events: none; + + &:hover { + opacity: 100%; + } + } + + .displayLocations { + * { + pointer-events: auto; + } + } + + .displayTiles { + pointer-events: auto; + display: flex; + flex-wrap: wrap; + button { + width: fit-content; + white-space: nowrap; + border: unset; + box-shadow: var(--box-shadow); + @include JTCS-accent-fill($primary-color: var(--data-art-color)); + &[data-is-default] { + @include JTCS-accent-fill($primary-color: var(--data-default-color)); + } + } + * { + pointer-events: auto; + } + legend { + background-color: inherit; + padding: 0.5rem; + } + display: flex; + flex-direction: row; + border: none; + color: white; + padding: 0rem 0rem; + min-width: 50%; + label:first-of-type { + border-top-left-radius: 8px; + border-bottom-left-radius: 8px; + } + label:last-of-type { + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + } + } +} +.sheet.actor, +.sheet.item { + .clickableImageContainer { + padding: unset; + margin: unset; + max-width: fit-content; + max-height: fit-content; + // flex: 0 !important; + + .clickableImageControls { + top: 0%; + transform: translateY(0%); + button.floating-control { + background: var(--bg-color); + border: unset; + } + } + } +} + +.clickableImage, +.rightClickableImage { + border: 0px solid transparent; + box-shadow: inset 0px 0px 2px transparent; + outline: 2px dotted transparent; + transition: all 100ms ease-out; + flex: 1; + &:hover { + border: 2px solid var(--JTCS-accent-color); + box-shadow: inset 2px 2px 8px rgba(50, 51, 59, 0.5); + // outline: 2px dotted var(--JTCS-accent-color); + cursor: pointer; + transition: all 100ms ease-out; + & ~ .clickableImageControls { + opacity: 100%; + // pointer-events: auto; + } + } + &:active { + box-shadow: inset 4px 4px 16px rgba(50, 51, 59, 0.5); + } +} + +.error { + &::before { + content: "⚠"; + color: var(--JTCS-danger-color); + } +} + +[data-missing="true"] { + &:hover { + color: var(--JTCS-warning-color); + } + color: var(--JTCS-danger-color); + + button:disabled { + opacity: 50%; + &:hover { + cursor: not-allowed; + } + } +} + +.unlinked-tiles-list { + padding-block: 2em; + h3 { + color: var(--accent-color); + } + ul { + background-color: var(--JTCS-elevation-BG-color); + } + li { + margin: unset; + list-style-type: none; + padding: unset; + .input-span[data-variant="visible-tick"] { + display: flex; + height: 100%; + width: 100%; + align-items: center; + input[type="radio"] { + margin-right: 0.5rem; + padding: 1rem; + & + label { + display: flex; + gap: 1rem; + color: var(--JTCS-text-color-on-bg); + border: unset; + border-bottom: 1px solid var(--JTCS-accent-color); + --border-color: var(--data-unlinked-color); + transition: color 150ms ease-in, background-color 150ms ease-in, + border-color 150ms ease-in; + .tick-box, + .tick-circle { + border-color: var(--JTCS-accent-color); + } + &:hover { + background-color: var(--data-unlinked-color); + color: var(--JTCS-text-color-on-fill); + border-color: var(--data-unlinked-color); + .tick-box, + .tick-circle { + transition: border-color 150ms ease-in; + border-color: var(--data-unlinked-color); + } + } + } + } + label { + padding: 1rem; + height: 100%; + width: 100%; + } + } + } +} diff --git a/templates/JTCS-settings-app.hbs b/templates/JTCS-settings-app.hbs new file mode 100644 index 0000000..eef087e --- /dev/null +++ b/templates/JTCS-settings-app.hbs @@ -0,0 +1,206 @@ +
+
+ + + + +
+
+
+ +
+ {{#with colorSchemeData}} +

{{this.name}}

+

{{{this.hint}}}

+
+ {{#each this.colors}} +
+ {{> (dynamicPartial "color-picker") + this + value=(lookup ../colors @key) + parentObjectName="colorSchemeData" + }} +
+ {{/each}} +
+ {{/with}} +
+ + +
+ {{#with indicatorColorData}} +

{{this.name}}

+

{{{this.hint}}}

+
+ {{#each this.colors}} +
+ {{> (dynamicPartial "color-picker") + this + value=(lookup ../colors @key) + parentObjectName="indicatorColorData" + }} +
+ {{/each}} +
+ {{/with}} +
+

Warning: The actions below are destructive or risky. Please proceed carefully.

+ + {{#with colorSchemeData}} +
+

Reset to Default Theme

+

+ Reset your colors to one of the default themes, in a dark or light scheme. +

+ + +
+
+

Toggle Auto-Contrast

+ {{#if (eq this.autoContrast true)}} +

+ This will make it so your chosen accent and gallery tile colors aren't automatically adjusted to contrast with your background color; however you risk choosing colors that make text illegible. +

+ + {{else}} +

This will cause your chosen accent and gallery tile colors to automatically adjust in brightness to contrast with your chosen background color.

+ + {{/if}} +
+ {{/with}} +
+ +
+ {{#with sheetSettings}} +

{{this.name}}

+

{{{this.hint}}}

+ {{> (dynamicPartial "fieldset-button-group") + type="checkbox" + variant="visible-tick" + choices=this.modularChoices + propertyString="sheetSettings.modularChoices" + extraClass="flex-wrap" + }} + {{/with}} +
+
+ +
+ {{#with fadeSheetImagesData}} +

{{this.name}}

+

{{{this.hint}}}

+ {{> (dynamicPartial "fieldset-button-group") + type="radio" + choices=this.choices + chosen=this.chosen + variant="visible-tick" + propertyString="fadeSheetImagesData.chosen" + }} + {{/with}} +
+
+ {{#with sheetFadeOpacityData}} +

{{name}}

+

+ {{{hint}}} +

+
+ {{rangePicker + value=this.value + name="sheetFadeOpacityData.value" + min=20 + max=100 + step=10 + }} +
+ {{/with}} +
+
+
+
+ {{#with dedicatedDisplayData}} +

{{this.name}}

+

{{{this.hint}}}

+
+ {{#with this.journal}} +

{{this.name}}

+

{{{this.hint}}}

+
+ + +
+ {{/with}} + {{> (dynamicPartial "checkbox") + propertyString=(getPropertyString @root "dedicatedDisplayData.journal" "autoActivate") + variant="visible-tick" + key="autoActivate" + checked=@root.dedicatedDisplayData.journal.autoActivate + }} + {{> (dynamicPartial "checkbox") + propertyString=(getPropertyString @root "dedicatedDisplayData.journal" "autoView") + variant="visible-tick" + key="autoView" + checked=@root.dedicatedDisplayData.journal.autoView + }} +
+
+ {{#with this.scene}} +

{{this.name}}

+

{{{this.hint}}}

+
+ + +
+ {{/with}} + {{> (dynamicPartial "checkbox") + propertyString=(getPropertyString @root "dedicatedDisplayData.scene" "autoActivate") + variant="visible-tick" + key="autoActivate" + checked=@root.dedicatedDisplayData.scene.autoActivate + }} + {{> (dynamicPartial "checkbox") + propertyString=(getPropertyString @root "dedicatedDisplayData.scene" "autoView") + variant="visible-tick" + key="autoView" + checked=@root.dedicatedDisplayData.scene.autoView + }} +
+ {{/with}} +
+
+ {{#with defaultTileImages}} +

{{this.name}}

+

{{{this.hint}}}

+ {{#each this.paths}} +
+ + + {{filePicker type="image" target=(concat "defaultTileImages.paths." @key)}} +
+ {{/each}} + {{/with}} +
+
+
+
+ + + +
+
\ No newline at end of file diff --git a/templates/badge.hbs b/templates/badge.hbs new file mode 100644 index 0000000..8547125 --- /dev/null +++ b/templates/badge.hbs @@ -0,0 +1,8 @@ +
+ + {{this.message}} +
\ No newline at end of file diff --git a/templates/checkbox-chip-group.hbs b/templates/checkbox-chip-group.hbs new file mode 100644 index 0000000..fc33a5c --- /dev/null +++ b/templates/checkbox-chip-group.hbs @@ -0,0 +1,21 @@ +
+ {{#each choices}} + + + + + {{/each}} +
\ No newline at end of file diff --git a/templates/checkbox.hbs b/templates/checkbox.hbs new file mode 100644 index 0000000..c69c29c --- /dev/null +++ b/templates/checkbox.hbs @@ -0,0 +1,21 @@ + + + + \ No newline at end of file diff --git a/templates/color-picker.hbs b/templates/color-picker.hbs new file mode 100644 index 0000000..c19897e --- /dev/null +++ b/templates/color-picker.hbs @@ -0,0 +1,30 @@ +
+
+ + +
+
+ {{#unless (eq @key "backgroundColor")}} + + {{/unless}} + + +
+
\ No newline at end of file diff --git a/templates/config.html b/templates/config.html new file mode 100644 index 0000000..3d8e21a --- /dev/null +++ b/templates/config.html @@ -0,0 +1,23 @@ +
+
+ + + + + +
+
+ + + + + +
+
+ + +
+
+ +
+
\ No newline at end of file diff --git a/templates/control-button.hbs b/templates/control-button.hbs new file mode 100644 index 0000000..9726574 --- /dev/null +++ b/templates/control-button.hbs @@ -0,0 +1,10 @@ +
+ +
\ No newline at end of file diff --git a/templates/delete-confirmation-prompt.hbs b/templates/delete-confirmation-prompt.hbs new file mode 100644 index 0000000..395dc97 --- /dev/null +++ b/templates/delete-confirmation-prompt.hbs @@ -0,0 +1,11 @@ +
+

{{this.heading}}

+ {{!--

Are you sure you want to {{this.destructiveActionText}}

--}} +

{{this.explanation}}

+
    + {{#each lossDataList as |item|}} +
  • {{item}}
  • + {{/each}} +
+

{{explanation2}}

+
\ No newline at end of file diff --git a/templates/display-tile-config.hbs b/templates/display-tile-config.hbs new file mode 100644 index 0000000..e30f4fa --- /dev/null +++ b/templates/display-tile-config.hbs @@ -0,0 +1,28 @@ +
+
+ + +
+
+ + +
+
+ +

This will keep the display tile from getting no larger than the tile that frames it

+ +
+
\ No newline at end of file diff --git a/templates/fieldset-button-group.hbs b/templates/fieldset-button-group.hbs new file mode 100644 index 0000000..fe62eb8 --- /dev/null +++ b/templates/fieldset-button-group.hbs @@ -0,0 +1,46 @@ +
+ {{#each this.choices}} + + + + + + {{/each}} +
\ No newline at end of file diff --git a/templates/icon-button.hbs b/templates/icon-button.hbs new file mode 100644 index 0000000..5b14299 --- /dev/null +++ b/templates/icon-button.hbs @@ -0,0 +1,13 @@ + \ No newline at end of file diff --git a/templates/image-controls.hbs b/templates/image-controls.hbs new file mode 100644 index 0000000..d3e630e --- /dev/null +++ b/templates/image-controls.hbs @@ -0,0 +1,48 @@ +
+ +
    + {{#each displayMethods as |method|}} +
  • + +
  • + {{/each}} +
+
+ {{#if displayTiles.length}} + {{#each displayTiles as |tileData|}} + + {{/each}} + {{else}} +

No Art Tiles in Scene

+ {{/if}} + +
+
\ No newline at end of file diff --git a/templates/input-span.hbs b/templates/input-span.hbs new file mode 100644 index 0000000..a46a234 --- /dev/null +++ b/templates/input-span.hbs @@ -0,0 +1,41 @@ + + + + \ No newline at end of file diff --git a/templates/input-with-error.hbs b/templates/input-with-error.hbs new file mode 100644 index 0000000..b01cd69 --- /dev/null +++ b/templates/input-with-error.hbs @@ -0,0 +1,12 @@ +
+ + +
\ No newline at end of file diff --git a/templates/item-list.hbs b/templates/item-list.hbs new file mode 100644 index 0000000..6a20a0f --- /dev/null +++ b/templates/item-list.hbs @@ -0,0 +1,23 @@ +
+ {{wrapInElement (wrapInElement @root.currentSceneName "span" "accent") "h3" "flex0"}} + {{!--

Art Tiles in "{{wrapInSpan currentSceneName}}"

--}} +
+
    + {{#each itemList as item}} + {{> (dynamicPartial "tile-list-item.hbs") + item + type=../type + currentSceneName=@root.currentSceneName + allFrameTiles=@root.frameTiles + }} + {{/each}} +
+ {{> (dynamicPartial "new-tile-list-item") type=../type }} +
+
diff --git a/templates/item-menu.hbs b/templates/item-menu.hbs new file mode 100644 index 0000000..5f8d443 --- /dev/null +++ b/templates/item-menu.hbs @@ -0,0 +1,21 @@ +
    + {{#each items as |item|}} + {{#if @last}} +
    + {{/if}} +
  • + + {{camelCaseToCapitalString @key}} +
  • + {{/each}} +
\ No newline at end of file diff --git a/templates/media-popout.html b/templates/media-popout.html new file mode 100644 index 0000000..5535c95 --- /dev/null +++ b/templates/media-popout.html @@ -0,0 +1,10 @@ +
+ {{#if isVideo}} +