diff --git a/.all-contributorsrc b/.all-contributorsrc new file mode 100644 index 00000000..0a5faad0 --- /dev/null +++ b/.all-contributorsrc @@ -0,0 +1,115 @@ +{ + "projectName": "JavaScript-Snake", + "projectOwner": "patorjk", + "files": ["README.md"], + "commitType": "docs", + "commitConvention": "angular", + "contributorsPerLine": 7, + "contributors": [ + { + "login": "patorjk", + "name": "patorjk", + "avatar_url": "https://avatars.githubusercontent.com/u/521224?v=4", + "profile": "http://patorjk.com/", + "contributions": ["code", "doc", "design", "bug", "example"] + }, + { + "login": "ultra17", + "name": "ultra17", + "avatar_url": "https://avatars.githubusercontent.com/u/27869698?v=4", + "profile": "https://github.com/ultra17", + "contributions": ["code", "doc", "design", "bug"] + }, + { + "login": "Rb64", + "name": "Rb64", + "avatar_url": "https://avatars.githubusercontent.com/u/91498309?v=4", + "profile": "https://github.com/Rb64", + "contributions": ["code", "bug"] + }, + { + "login": "legoman8304", + "name": "Wyatt Nulton", + "avatar_url": "https://avatars.githubusercontent.com/u/43346988?v=4", + "profile": "https://github.com/legoman8304", + "contributions": ["code", "bug"] + }, + { + "login": "ashishsiot", + "name": "Ashish Bhoir", + "avatar_url": "https://avatars.githubusercontent.com/u/63919950?v=4", + "profile": "https://github.com/ashishsiot", + "contributions": ["doc"] + }, + { + "login": "dginovker", + "name": "Dan G", + "avatar_url": "https://avatars.githubusercontent.com/u/32943174?v=4", + "profile": "http://dginovker.github.io", + "contributions": ["code", "bug"] + }, + { + "login": "Megas4ever", + "name": "Megas4ever", + "avatar_url": "https://avatars.githubusercontent.com/u/28103886?v=4", + "profile": "https://github.com/Megas4ever", + "contributions": ["code", "design"] + }, + { + "login": "mamamia5x", + "name": "Bugs Bunny", + "avatar_url": "https://avatars.githubusercontent.com/u/57536929?v=4", + "profile": "https://github.com/mamamia5x", + "contributions": ["code", "bug"] + }, + { + "login": "Coteh", + "name": "James Cote", + "avatar_url": "https://avatars.githubusercontent.com/u/3276350?v=4", + "profile": "https://www.jamescote.ca", + "contributions": ["code", "bug", "doc"] + }, + { + "login": "yokesharun", + "name": "Arun Yokesh", + "avatar_url": "https://avatars.githubusercontent.com/u/12830078?v=4", + "profile": "http://yokesharun.github.io/", + "contributions": ["code", "design"] + }, + { + "login": "GregFrench", + "name": "Greg French", + "avatar_url": "https://avatars.githubusercontent.com/u/17938510?v=4", + "profile": "https://github.com/GregFrench", + "contributions": ["code"] + }, + { + "login": "KT360", + "name": "KT360", + "avatar_url": "https://avatars.githubusercontent.com/u/31077743?v=4", + "profile": "https://github.com/KT360", + "contributions": ["code", "design"] + }, + { + "login": "Thusal06", + "name": "Thusal Ranawaka", + "avatar_url": "https://avatars.githubusercontent.com/u/66709891?v=4", + "profile": "https://thusal06.github.io/", + "contributions": ["code", "design"] + }, + { + "login": "Furtano", + "name": "C. S.", + "avatar_url": "https://avatars.githubusercontent.com/u/4115133?v=4", + "profile": "https://github.com/Furtano", + "contributions": ["code", "design"] + }, + { + "login": "akhill2606", + "name": "Akhil Manohar", + "avatar_url": "https://avatars.githubusercontent.com/u/56164681?v=4", + "profile": "https://github.com/akhill2606", + "contributions": ["code"] + } + ] +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..f78ec928 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.parcel-cache diff --git a/.parcelrc b/.parcelrc new file mode 100644 index 00000000..1f3df894 --- /dev/null +++ b/.parcelrc @@ -0,0 +1,4 @@ +{ + "extends": ["@parcel/config-default"], + "reporters": ["...", "parcel-reporter-static-files-copy"] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..7cf19bc5 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright © Patrick Gillespie, http://patorjk.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 8bb716a5..0956f864 --- a/README.md +++ b/README.md @@ -1,19 +1,121 @@ -JavaScript Snake
-By Patrick Gillespie
-License: MIT
+# JavaScript Snake Game + +This is a DOM-based game of Snake that I wrote in JavaScript over a decade ago. It was made to have sort of a nostalgic feel to it. + +## Play and Edit the Game Online! + +You can now play and edit the game live in codesandbox: + +https://codesandbox.io/s/github/patorjk/JavaScript-Snake?file=/index.html + +**2025: Looks like CSS inside the sandbox isn't working properly, in the game it works fine though** + +On first load sometimes the game frame will not load correctly and you'll need to press the refresh icon above its display panel to get the game to show. + +Original game is located here: + http://patorjk.com/games/snake -This is a DOM-based game of Snake that I wrote in JavaScript a few years back. +## How to use -Other than the full screen mode demonstrated in the code, it can also be -initialized in div tags within a page. Example: +The index.html file should give an idea of how to use this code. However, below you can see how to initialize it into any div within a webpage. - var mySnakeBoard = new SNAKE.Board( { + const mySnakeBoard = new SNAKE.Board( { boardContainer: "game-area", fullScreen: false, width: 580, height:400 }); - -The comments are formatted a little strange because at the time I was playing -around with YUI Doc. \ No newline at end of file + +The comments within the source code are formatted a little strange because at the time I was playing around with YUI Doc which generates documentation from code. Kind of sucks that there's so much churn in the JavaScript world. However, I'm glad the rest of the code doesn't use any external libraries, as this game still works the same after over a decade. + +## Running + +Clone project, then at command line: + +``` +npx parcel src/index.html +``` + +Runs on http://localhost:1234 + +## AI Snake + +If you want to control the snake via an AI algorithm see the ai-init.js and ai-example files. + +Essentially all you have to do is run `params.startAIGame();` when initializing and pass in a `moveSnakeWithAI` method +which is run before the snake does each move. + +```js + moveSnakeWithAI: ({ + grid, + snakeHead, + currentDirection, + isFirstGameMove, + setDirection, + }) => { + + /* + Direction: + 0 + 3 1 + 2 + */ + + // This is NOT a real hamiltonian cycle. It misses some values, I'm just including this here as an example of + // a look-up type table that you could do. + const hamiltonianCycleGrid = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0], + [0, 0, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 0], + [0, 0, 2, 0, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0], + [0, 0, 2, 0, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0], + [0, 0, 3, 0, 3, 3, 3, 3, 0, 3, 0, 3, 0, 3, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ]; + + const newDirection = hamiltonianCycleGrid[snakeHead.row][snakeHead.col]; + setDirection(newDirection); + }, + onInit: (params) => { + params.startAIGame(); // This start an AI game + }, +``` + +## Contributors + +Thanks goes to these people: ([emoji key](https://allcontributors.org/docs/en/emoji-key)) + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
patorjk
patorjk

💻 📖 🎨 🐛 💡
ultra17
ultra17

💻 📖 🎨 🐛
Rb64
Rb64

💻 🐛
Wyatt Nulton
Wyatt Nulton

💻 🐛
Ashish Bhoir
Ashish Bhoir

📖
Dan G
Dan G

💻 🐛
Megas4ever
Megas4ever

💻 🎨
Bugs Bunny
Bugs Bunny

💻 🐛
James Cote
James Cote

💻 🐛 📖
Arun Yokesh
Arun Yokesh

💻 🎨
Greg French
Greg French

💻
KT360
KT360

💻 🎨
Thusal Ranawaka
Thusal Ranawaka

💻 🎨
C. S.
C. S.

💻 🎨
Akhil Manohar
Akhil Manohar

💻
+ + + + + diff --git a/css/snake.css b/css/snake.css deleted file mode 100644 index dc80d4bb..00000000 --- a/css/snake.css +++ /dev/null @@ -1,114 +0,0 @@ -/* -JavaScript Snake -By Patrick Gillespie -http://patorjk.com/games/snake -*/ -body { - margin:0px; - padding:0px; -} - -#game-area { - margin:0px; - padding:0px; -} - -#game-area:focus { outline: none; } - -a.snake-link, a.snake-link:link, a.snake-link:visited { - color: #FCFC54; -} - -a.snake-link:hover { - color: #FfFf54; -} - - -.snake-panel-component { - position: absolute; - font-family: Verdana, arial, helvetica, sans-serif; - font-size: 14px; - color: #ffffff; - text-align: center; - background-color: #FC5454; - padding: 8px; - margin: 0px; -} - -.snake-snakebody-block { - margin: 0px; - padding: 0px; - background-color: #FF0000; - position: absolute; - border: 0px solid #000080; - background-repeat: no-repeat; -} - -.snake-snakebody-alive { - background-image: url('./images/snakeblock.png'); -} -.snake-snakebody-dead { - background-image: url('./images/deadblock.png'); -} - -.snake-food-block { - margin: 0px; - padding: 0px; - background-color: #FF0000; - border: 0px solid #000080; - position: absolute; -} - -.snake-playing-field { - margin: 0px; - padding: 0px; - position: absolute; - background-color: #0000A8; - border: 0px solid #0000A8; -} - -.snake-game-container { - margin: 0px; - padding: 0px; - border-width: 0px; - border-style: none; - zoom: 1; - background-color: #FC5454; - position: relative; -} - -.snake-welcome-dialog { - padding: 8px; - margin: 0px; - background-color: #000000; - color: #ffffff; - font-family: Verdana, arial, helvetica, sans-serif; - font-size: 14px; - position: absolute; - top: 50%; - left: 50%; - width: 300px; - height: 150px; - margin-top: -100px; - margin-left: -158px; - text-align: center; - display: block; -} - -.snake-try-again-dialog { - padding: 8px; - margin: 0px; - background-color: #000000; - color: #ffffff; - font-family: Verdana, arial, helvetica, sans-serif; - font-size: 14px; - position: absolute; - top: 50%; - left: 50%; - width: 300px; - height: 100px; - margin-top: -75px; - margin-left: -158px; - text-align: center; - display: none; -} \ No newline at end of file diff --git a/index.htm b/index.htm deleted file mode 100644 index 39921570..00000000 --- a/index.htm +++ /dev/null @@ -1,31 +0,0 @@ - - - - - JavaScript Snake - - - -
-
- - - - \ No newline at end of file diff --git a/js/snake.js b/js/snake.js deleted file mode 100644 index aa0be7a5..00000000 --- a/js/snake.js +++ /dev/null @@ -1,925 +0,0 @@ -/* -JavaScript Snake -By Patrick Gillespie -http://patorjk.com/games/snake -*/ - -/** -* @module Snake -* @class SNAKE -*/ - -var SNAKE = SNAKE || {}; - -/** -* @method addEventListener -* @param {Object} obj The object to add an event listener to. -* @param {String} event The event to listen for. -* @param {Function} funct The function to execute when the event is triggered. -* @param {Boolean} evtCapturing True to do event capturing, false to do event bubbling. -*/ - -SNAKE.addEventListener = (function() { - if (window.addEventListener) { - return function(obj, event, funct, evtCapturing) { - obj.addEventListener(event, funct, evtCapturing); - }; - } else if (window.attachEvent) { - return function(obj, event, funct) { - obj.attachEvent("on" + event, funct); - }; - } -})(); - -/** -* @method removeEventListener -* @param {Object} obj The object to remove an event listener from. -* @param {String} event The event that was listened for. -* @param {Function} funct The function that was executed when the event is triggered. -* @param {Boolean} evtCapturing True if event capturing was done, false otherwise. -*/ - -SNAKE.removeEventListener = (function() { - if (window.removeEventListener) { - return function(obj, event, funct, evtCapturing) { - obj.removeEventListener(event, funct, evtCapturing); - }; - } else if (window.detachEvent) { - return function(obj, event, funct) { - obj.detachEvent("on" + event, funct); - }; - } -})(); - -/** -* This class manages the snake which will reside inside of a SNAKE.Board object. -* @class Snake -* @constructor -* @namespace SNAKE -* @param {Object} config The configuration object for the class. Contains playingBoard (the SNAKE.Board that this snake resides in), startRow and startCol. -*/ -SNAKE.Snake = SNAKE.Snake || (function() { - - // ------------------------------------------------------------------------- - // Private static variables and methods - // ------------------------------------------------------------------------- - - var instanceNumber = 0; - var blockPool = []; - - var SnakeBlock = function() { - this.elm = null; - this.elmStyle = null; - this.row = -1; - this.col = -1; - this.xPos = -1000; - this.yPos = -1000; - this.next = null; - this.prev = null; - }; - - // this function is adapted from the example at http://greengeckodesign.com/blog/2007/07/get-highest-z-index-in-javascript.html - function getNextHighestZIndex(myObj) { - var highestIndex = 0, - currentIndex = 0, - ii; - for (ii in myObj) { - if (myObj[ii].elm.currentStyle){ - currentIndex = parseFloat(myObj[ii].elm.style["z-index"],10); - }else if(window.getComputedStyle) { - currentIndex = parseFloat(document.defaultView.getComputedStyle(myObj[ii].elm,null).getPropertyValue("z-index"),10); - } - if(!isNaN(currentIndex) && currentIndex > highestIndex){ - highestIndex = currentIndex; - } - } - return(highestIndex+1); - } - - // ------------------------------------------------------------------------- - // Contructor + public and private definitions - // ------------------------------------------------------------------------- - - /* - config options: - playingBoard - the SnakeBoard that this snake belongs too. - startRow - The row the snake should start on. - startCol - The column the snake should start on. - */ - return function(config) { - - if (!config||!config.playingBoard) {return;} - - // ----- private variables ----- - - var me = this, - playingBoard = config.playingBoard, - myId = instanceNumber++, - growthIncr = 5, - moveQueue = [], // a queue that holds the next moves of the snake - currentDirection = 1, // 0: up, 1: left, 2: down, 3: right - columnShift = [0, 1, 0, -1], - rowShift = [-1, 0, 1, 0], - xPosShift = [], - yPosShift = [], - snakeSpeed = 75, - isDead = false; - - // ----- public variables ----- - - me.snakeBody = {}; - me.snakeBody["b0"] = new SnakeBlock(); // create snake head - me.snakeBody["b0"].row = config.startRow || 1; - me.snakeBody["b0"].col = config.startCol || 1; - me.snakeBody["b0"].xPos = me.snakeBody["b0"].row * playingBoard.getBlockWidth(); - me.snakeBody["b0"].yPos = me.snakeBody["b0"].col * playingBoard.getBlockHeight(); - me.snakeBody["b0"].elm = createSnakeElement(); - me.snakeBody["b0"].elmStyle = me.snakeBody["b0"].elm.style; - playingBoard.getBoardContainer().appendChild( me.snakeBody["b0"].elm ); - me.snakeBody["b0"].elm.style.left = me.snakeBody["b0"].xPos + "px"; - me.snakeBody["b0"].elm.style.top = me.snakeBody["b0"].yPos + "px"; - me.snakeBody["b0"].next = me.snakeBody["b0"]; - me.snakeBody["b0"].prev = me.snakeBody["b0"]; - - me.snakeLength = 1; - me.snakeHead = me.snakeBody["b0"]; - me.snakeTail = me.snakeBody["b0"]; - me.snakeHead.elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/,''); - me.snakeHead.elm.className += " snake-snakebody-alive"; - - // ----- private methods ----- - - function createSnakeElement() { - var tempNode = document.createElement("div"); - tempNode.className = "snake-snakebody-block"; - tempNode.style.left = "-1000px"; - tempNode.style.top = "-1000px"; - tempNode.style.width = playingBoard.getBlockWidth() + "px"; - tempNode.style.height = playingBoard.getBlockHeight() + "px"; - return tempNode; - } - - function createBlocks(num) { - var tempBlock; - var tempNode = createSnakeElement(); - - for (var ii = 1; ii < num; ii++){ - tempBlock = new SnakeBlock(); - tempBlock.elm = tempNode.cloneNode(true); - tempBlock.elmStyle = tempBlock.elm.style; - playingBoard.getBoardContainer().appendChild( tempBlock.elm ); - blockPool[blockPool.length] = tempBlock; - } - - tempBlock = new SnakeBlock(); - tempBlock.elm = tempNode; - playingBoard.getBoardContainer().appendChild( tempBlock.elm ); - blockPool[blockPool.length] = tempBlock; - } - - // ----- public methods ----- - - /** - * This method is called when a user presses a key. It logs arrow key presses in "moveQueue", which is used when the snake needs to make its next move. - * @method handleArrowKeys - * @param {Number} keyNum A number representing the key that was pressed. - */ - /* - Handles what happens when an arrow key is pressed. - Direction explained (0 = up, etc etc) - 0 - 3 1 - 2 - */ - me.handleArrowKeys = function(keyNum) { - if (isDead) {return;} - - var snakeLength = me.snakeLength; - var lastMove = moveQueue[0] || currentDirection; - - switch (keyNum) { - case 37: - if ( lastMove !== 1 || snakeLength === 1 ) { - moveQueue.unshift(3); //SnakeDirection = 3; - } - break; - case 38: - if ( lastMove !== 2 || snakeLength === 1 ) { - moveQueue.unshift(0);//SnakeDirection = 0; - } - break; - case 39: - if ( lastMove !== 3 || snakeLength === 1 ) { - moveQueue.unshift(1); //SnakeDirection = 1; - } - break; - case 40: - if ( lastMove !== 0 || snakeLength === 1 ) { - moveQueue.unshift(2);//SnakeDirection = 2; - } - break; - } - }; - - /** - * This method is executed for each move of the snake. It determines where the snake will go and what will happen to it. This method needs to run quickly. - * @method go - */ - me.go = function() { - - var oldHead = me.snakeHead, - newHead = me.snakeTail, - myDirection = currentDirection, - grid = playingBoard.grid; // cache grid for quicker lookup - - me.snakeTail = newHead.prev; - me.snakeHead = newHead; - - // clear the old board position - if ( grid[newHead.row] && grid[newHead.row][newHead.col] ) { - grid[newHead.row][newHead.col] = 0; - } - - if (moveQueue.length){ - myDirection = currentDirection = moveQueue.pop(); - } - - newHead.col = oldHead.col + columnShift[myDirection]; - newHead.row = oldHead.row + rowShift[myDirection]; - newHead.xPos = oldHead.xPos + xPosShift[myDirection]; - newHead.yPos = oldHead.yPos + yPosShift[myDirection]; - - if ( !newHead.elmStyle ) { - newHead.elmStyle = newHead.elm.style; - } - - newHead.elmStyle.left = newHead.xPos + "px"; - newHead.elmStyle.top = newHead.yPos + "px"; - - // check the new spot the snake moved into - - if (grid[newHead.row][newHead.col] === 0) { - grid[newHead.row][newHead.col] = 1; - setTimeout(function(){me.go();}, snakeSpeed); - } else if (grid[newHead.row][newHead.col] > 0) { - me.handleDeath(); - } else if (grid[newHead.row][newHead.col] === playingBoard.getGridFoodValue()) { - grid[newHead.row][newHead.col] = 1; - me.eatFood(); - setTimeout(function(){me.go();}, snakeSpeed); - } - }; - - /** - * This method is called when it is determined that the snake has eaten some food. - * @method eatFood - */ - me.eatFood = function() { - if (blockPool.length <= growthIncr) { - createBlocks(growthIncr*2); - } - var blocks = blockPool.splice(0, growthIncr); - - var ii = blocks.length, - index, - prevNode = me.snakeTail; - while (ii--) { - index = "b" + me.snakeLength++; - me.snakeBody[index] = blocks[ii]; - me.snakeBody[index].prev = prevNode; - me.snakeBody[index].elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/,'') - me.snakeBody[index].elm.className += " snake-snakebody-alive"; - prevNode.next = me.snakeBody[index]; - prevNode = me.snakeBody[index]; - } - me.snakeTail = me.snakeBody[index]; - me.snakeTail.next = me.snakeHead; - me.snakeHead.prev = me.snakeTail; - - playingBoard.foodEaten(); - }; - - /** - * This method handles what happens when the snake dies. - * @method handleDeath - */ - me.handleDeath = function() { - me.snakeHead.elm.style.zIndex = getNextHighestZIndex(me.snakeBody); - me.snakeHead.elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-alive\b/,'') - me.snakeHead.elm.className += " snake-snakebody-dead"; - - isDead = true; - playingBoard.handleDeath(); - moveQueue.length = 0; - }; - - /** - * This method sets a flag that lets the snake be alive again. - * @method rebirth - */ - me.rebirth = function() { - isDead = false; - }; - - /** - * This method reset the snake so it is ready for a new game. - * @method reset - */ - me.reset = function() { - if (isDead === false) {return;} - - var blocks = [], - curNode = me.snakeHead.next, - nextNode; - while (curNode !== me.snakeHead) { - nextNode = curNode.next; - curNode.prev = null; - curNode.next = null; - blocks.push(curNode); - curNode = nextNode; - } - me.snakeHead.next = me.snakeHead; - me.snakeHead.prev = me.snakeHead; - me.snakeTail = me.snakeHead; - me.snakeLength = 1; - - for (var ii = 0; ii < blocks.length; ii++) { - blocks[ii].elm.style.left = "-1000px"; - blocks[ii].elm.style.top = "-1000px"; - blocks[ii].elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/,'') - blocks[ii].elm.className += " snake-snakebody-alive"; - } - - blockPool.concat(blocks); - me.snakeHead.elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/,'') - me.snakeHead.elm.className += " snake-snakebody-alive"; - me.snakeHead.row = config.startRow || 1; - me.snakeHead.col = config.startCol || 1; - me.snakeHead.xPos = me.snakeHead.row * playingBoard.getBlockWidth(); - me.snakeHead.yPos = me.snakeHead.col * playingBoard.getBlockHeight(); - me.snakeHead.elm.style.left = me.snakeHead.xPos + "px"; - me.snakeHead.elm.style.top = me.snakeHead.yPos + "px"; - }; - - // --------------------------------------------------------------------- - // Initialize - // --------------------------------------------------------------------- - - createBlocks(growthIncr*2); - xPosShift[0] = 0; - xPosShift[1] = playingBoard.getBlockWidth(); - xPosShift[2] = 0; - xPosShift[3] = -1 * playingBoard.getBlockWidth(); - - yPosShift[0] = -1 * playingBoard.getBlockHeight(); - yPosShift[1] = 0; - yPosShift[2] = playingBoard.getBlockHeight(); - yPosShift[3] = 0; - }; -})(); - -/** -* This class manages the food which the snake will eat. -* @class Food -* @constructor -* @namespace SNAKE -* @param {Object} config The configuration object for the class. Contains playingBoard (the SNAKE.Board that this food resides in). -*/ - -SNAKE.Food = SNAKE.Food || (function() { - - // ------------------------------------------------------------------------- - // Private static variables and methods - // ------------------------------------------------------------------------- - - var instanceNumber = 0; - - function getRandomPosition(x, y){ - return Math.floor(Math.random()*(y+1-x)) + x; - } - - // ------------------------------------------------------------------------- - // Contructor + public and private definitions - // ------------------------------------------------------------------------- - - /* - config options: - playingBoard - the SnakeBoard that this object belongs too. - */ - return function(config) { - - if (!config||!config.playingBoard) {return;} - - // ----- private variables ----- - - var me = this; - var playingBoard = config.playingBoard; - var fRow, fColumn; - var myId = instanceNumber++; - - var elmFood = document.createElement("div"); - elmFood.setAttribute("id", "snake-food-"+myId); - elmFood.className = "snake-food-block"; - elmFood.style.width = playingBoard.getBlockWidth() + "px"; - elmFood.style.height = playingBoard.getBlockHeight() + "px"; - elmFood.style.left = "-1000px"; - elmFood.style.top = "-1000px"; - playingBoard.getBoardContainer().appendChild(elmFood); - - // ----- public methods ----- - - /** - * @method getFoodElement - * @return {DOM Element} The div the represents the food. - */ - me.getFoodElement = function() { - return elmFood; - }; - - /** - * Randomly places the food onto an available location on the playing board. - * @method randomlyPlaceFood - */ - me.randomlyPlaceFood = function() { - // if there exist some food, clear its presence from the board - if (playingBoard.grid[fRow] && playingBoard.grid[fRow][fColumn] === playingBoard.getGridFoodValue()){ - playingBoard.grid[fRow][fColumn] = 0; - } - - var row = 0, col = 0, numTries = 0; - - var maxRows = playingBoard.grid.length-1; - var maxCols = playingBoard.grid[0].length-1; - - while (playingBoard.grid[row][col] !== 0){ - row = getRandomPosition(1, maxRows); - col = getRandomPosition(1, maxCols); - - // in some cases there may not be any room to put food anywhere - // instead of freezing, exit out - numTries++; - if (numTries > 20000){ - row = -1; - col = -1; - break; - } - } - - playingBoard.grid[row][col] = playingBoard.getGridFoodValue(); - fRow = row; - fColumn = col; - elmFood.style.top = row * playingBoard.getBlockHeight() + "px"; - elmFood.style.left = col * playingBoard.getBlockWidth() + "px"; - }; - }; -})(); - -/** -* This class manages playing board for the game. -* @class Board -* @constructor -* @namespace SNAKE -* @param {Object} config The configuration object for the class. Set fullScreen equal to true if you want the game to take up the full screen, otherwise, set the top, left, width and height parameters. -*/ - -SNAKE.Board = SNAKE.Board || (function() { - - // ------------------------------------------------------------------------- - // Private static variables and methods - // ------------------------------------------------------------------------- - - var instanceNumber = 0; - - // this function is adapted from the example at http://greengeckodesign.com/blog/2007/07/get-highest-z-index-in-javascript.html - function getNextHighestZIndex(myObj) { - var highestIndex = 0, - currentIndex = 0, - ii; - for (ii in myObj) { - if (myObj[ii].elm.currentStyle){ - currentIndex = parseFloat(myObj[ii].elm.style["z-index"],10); - }else if(window.getComputedStyle) { - currentIndex = parseFloat(document.defaultView.getComputedStyle(myObj[ii].elm,null).getPropertyValue("z-index"),10); - } - if(!isNaN(currentIndex) && currentIndex > highestIndex){ - highestIndex = currentIndex; - } - } - return(highestIndex+1); - } - - /* - This function returns the width of the available screen real estate that we have - */ - function getClientWidth(){ - var myWidth = 0; - if( typeof window.innerWidth === "number" ) { - myWidth = window.innerWidth;//Non-IE - } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { - myWidth = document.documentElement.clientWidth;//IE 6+ in 'standards compliant mode' - } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { - myWidth = document.body.clientWidth;//IE 4 compatible - } - return myWidth; - } - /* - This function returns the height of the available screen real estate that we have - */ - function getClientHeight(){ - var myHeight = 0; - if( typeof window.innerHeight === "number" ) { - myHeight = window.innerHeight;//Non-IE - } else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { - myHeight = document.documentElement.clientHeight;//IE 6+ in 'standards compliant mode' - } else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { - myHeight = document.body.clientHeight;//IE 4 compatible - } - return myHeight; - } - - // ------------------------------------------------------------------------- - // Contructor + public and private definitions - // ------------------------------------------------------------------------- - - return function(inputConfig) { - - // --- private variables --- - var me = this, - myId = instanceNumber++, - config = inputConfig || {}, - MAX_BOARD_COLS = 250, - MAX_BOARD_ROWS = 250, - blockWidth = 20, - blockHeight = 20, - GRID_FOOD_VALUE = -1, // the value of a spot on the board that represents snake food, MUST BE NEGATIVE - myFood, - mySnake, - boardState = 1, // 0: in active; 1: awaiting game start; 2: playing game - myKeyListener, - // Board components - elmContainer, elmPlayingField, elmAboutPanel, elmLengthPanel, elmWelcome, elmTryAgain; - - // --- public variables --- - me.grid = []; - - // --------------------------------------------------------------------- - // private functions - // --------------------------------------------------------------------- - - function createBoardElements() { - elmPlayingField = document.createElement("div"); - elmPlayingField.setAttribute("id", "playingField"); - elmPlayingField.className = "snake-playing-field"; - - SNAKE.addEventListener(elmPlayingField, "click", function() { - elmContainer.focus(); - }, false); - - elmAboutPanel = document.createElement("div"); - elmAboutPanel.className = "snake-panel-component"; - elmAboutPanel.innerHTML = "more patorjk.com apps - source code"; - - elmLengthPanel = document.createElement("div"); - elmLengthPanel.className = "snake-panel-component"; - elmLengthPanel.innerHTML = "Length: 1"; - - elmWelcome = createWelcomeElement(); - elmTryAgain = createTryAgainElement(); - - SNAKE.addEventListener( elmContainer, "keyup", function(evt) { - if (!evt) var evt = window.event; - evt.cancelBubble = true; - if (evt.stopPropagation) {evt.stopPropagation();} - if (evt.preventDefault) {evt.preventDefault();} - return false; - }, false); - - elmContainer.className = "snake-game-container"; - - elmContainer.appendChild(elmPlayingField); - elmContainer.appendChild(elmAboutPanel); - elmContainer.appendChild(elmLengthPanel); - elmContainer.appendChild(elmWelcome); - elmContainer.appendChild(elmTryAgain); - - mySnake = new SNAKE.Snake({playingBoard:me,startRow:2,startCol:2}); - myFood = new SNAKE.Food({playingBoard: me}); - - elmWelcome.style.zIndex = 1000; - } - function maxBoardWidth() { - return MAX_BOARD_COLS * me.getBlockWidth(); - } - function maxBoardHeight() { - return MAX_BOARD_ROWS * me.getBlockHeight(); - } - - function createWelcomeElement() { - var tmpElm = document.createElement("div"); - tmpElm.id = "sbWelcome" + myId; - tmpElm.className = "snake-welcome-dialog"; - - var welcomeTxt = document.createElement("div"); - var fullScreenText = ""; - if (config.fullScreen) { - fullScreenText = "On Windows, press F11 to play in Full Screen mode."; - } - welcomeTxt.innerHTML = "JavaScript Snake

Use the arrow keys on your keyboard to play the game. " + fullScreenText + "

"; - var welcomeStart = document.createElement("button"); - welcomeStart.appendChild( document.createTextNode("Play Game")); - - var loadGame = function() { - SNAKE.removeEventListener(window, "keyup", kbShortcut, false); - tmpElm.style.display = "none"; - me.setBoardState(1); - me.getBoardContainer().focus(); - }; - - var kbShortcut = function(evt) { - if (!evt) var evt = window.event; - var keyNum = (evt.which) ? evt.which : evt.keyCode; - if (keyNum === 32 || keyNum === 13) { - loadGame(); - } - }; - SNAKE.addEventListener(window, "keyup", kbShortcut, false); - SNAKE.addEventListener(welcomeStart, "click", loadGame, false); - - tmpElm.appendChild(welcomeTxt); - tmpElm.appendChild(welcomeStart); - return tmpElm; - } - - function createTryAgainElement() { - var tmpElm = document.createElement("div"); - tmpElm.id = "sbTryAgain" + myId; - tmpElm.className = "snake-try-again-dialog"; - - var tryAgainTxt = document.createElement("div"); - tryAgainTxt.innerHTML = "JavaScript Snake

You died :(.

"; - var tryAgainStart = document.createElement("button"); - tryAgainStart.appendChild( document.createTextNode("Play Again?")); - - var reloadGame = function() { - tmpElm.style.display = "none"; - me.resetBoard(); - me.setBoardState(1); - me.getBoardContainer().focus(); - }; - - var kbTryAgainShortcut = function(evt) { - if (boardState !== 0 || tmpElm.style.display !== "block") {return;} - if (!evt) var evt = window.event; - var keyNum = (evt.which) ? evt.which : evt.keyCode; - if (keyNum === 32 || keyNum === 13) { - reloadGame(); - } - }; - SNAKE.addEventListener(window, "keyup", kbTryAgainShortcut, true); - - SNAKE.addEventListener(tryAgainStart, "click", reloadGame, false); - tmpElm.appendChild(tryAgainTxt); - tmpElm.appendChild(tryAgainStart); - return tmpElm; - } - - // --------------------------------------------------------------------- - // public functions - // --------------------------------------------------------------------- - - /** - * Resets the playing board for a new game. - * @method resetBoard - */ - me.resetBoard = function() { - SNAKE.removeEventListener(elmContainer, "keydown", myKeyListener, false); - mySnake.reset(); - elmLengthPanel.innerHTML = "Length: 1"; - me.setupPlayingField(); - }; - /** - * Gets the current state of the playing board. There are 3 states: 0 - Welcome or Try Again dialog is present. 1 - User has pressed "Start Game" on the Welcome or Try Again dialog but has not pressed an arrow key to move the snake. 2 - The game is in progress and the snake is moving. - * @method getBoardState - * @return {Number} The state of the board. - */ - me.getBoardState = function() { - return boardState; - }; - /** - * Sets the current state of the playing board. There are 3 states: 0 - Welcome or Try Again dialog is present. 1 - User has pressed "Start Game" on the Welcome or Try Again dialog but has not pressed an arrow key to move the snake. 2 - The game is in progress and the snake is moving. - * @method setBoardState - * @param {Number} state The state of the board. - */ - me.setBoardState = function(state) { - boardState = state; - }; - /** - * @method getGridFoodValue - * @return {Number} A number that represents food on a number representation of the playing board. - */ - me.getGridFoodValue = function() { - return GRID_FOOD_VALUE; - }; - /** - * @method getPlayingFieldElement - * @return {DOM Element} The div representing the playing field (this is where the snake can move). - */ - me.getPlayingFieldElement = function() { - return elmPlayingField; - }; - /** - * @method setBoardContainer - * @param {DOM Element or String} myContainer Sets the container element for the game. - */ - me.setBoardContainer = function(myContainer) { - if (typeof myContainer === "string") { - myContainer = document.getElementById(myContainer); - } - if (myContainer === elmContainer) {return;} - elmContainer = myContainer; - elmPlayingField = null; - - me.setupPlayingField(); - }; - /** - * @method getBoardContainer - * @return {DOM Element} - */ - me.getBoardContainer = function() { - return elmContainer; - }; - /** - * @method getBlockWidth - * @return {Number} - */ - me.getBlockWidth = function() { - return blockWidth; - }; - /** - * @method getBlockHeight - * @return {Number} - */ - me.getBlockHeight = function() { - return blockHeight; - }; - /** - * Sets up the playing field. - * @method setupPlayingField - */ - me.setupPlayingField = function () { - - if (!elmPlayingField) {createBoardElements();} // create playing field - - // calculate width of our game container - var cWidth, cHeight; - if (config.fullScreen === true) { - cTop = 0; - cLeft = 0; - cWidth = getClientWidth()-5; - cHeight = getClientHeight()-5; - document.body.style.backgroundColor = "#FC5454"; - } else { - cTop = config.top; - cLeft = config.left; - cWidth = config.width; - cHeight = config.height; - } - - // define the dimensions of the board and playing field - var wEdgeSpace = me.getBlockWidth()*2 + (cWidth % me.getBlockWidth()); - var fWidth = Math.min(maxBoardWidth()-wEdgeSpace,cWidth-wEdgeSpace); - var hEdgeSpace = me.getBlockHeight()*3 + (cHeight % me.getBlockHeight()); - var fHeight = Math.min(maxBoardHeight()-hEdgeSpace,cHeight-hEdgeSpace); - - elmContainer.style.left = cLeft + "px"; - elmContainer.style.top = cTop + "px"; - elmContainer.style.width = cWidth + "px"; - elmContainer.style.height = cHeight + "px"; - elmPlayingField.style.left = me.getBlockWidth() + "px"; - elmPlayingField.style.top = me.getBlockHeight() + "px"; - elmPlayingField.style.width = fWidth + "px"; - elmPlayingField.style.height = fHeight + "px"; - - // the math for this will need to change depending on font size, padding, etc - // assuming height of 14 (font size) + 8 (padding) - var bottomPanelHeight = hEdgeSpace - me.getBlockHeight(); - var pLabelTop = me.getBlockHeight() + fHeight + Math.round((bottomPanelHeight - 30)/2) + "px"; - - elmAboutPanel.style.top = pLabelTop; - elmAboutPanel.style.width = "450px"; - elmAboutPanel.style.left = Math.round(cWidth/2) - Math.round(450/2) + "px"; - - elmLengthPanel.style.top = pLabelTop; - elmLengthPanel.style.left = cWidth - 120 + "px"; - - // if width is too narrow, hide the about panel - if (cWidth < 700) { - elmAboutPanel.style.display = "none"; - } else { - elmAboutPanel.style.display = "block"; - } - - me.grid = []; - var numBoardCols = fWidth / me.getBlockWidth() + 2; - var numBoardRows = fHeight / me.getBlockHeight() + 2; - - for (var row = 0; row < numBoardRows; row++) { - me.grid[row] = []; - for (var col = 0; col < numBoardCols; col++) { - if (col === 0 || row === 0 || col === (numBoardCols-1) || row === (numBoardRows-1)) { - me.grid[row][col] = 1; // an edge - } else { - me.grid[row][col] = 0; // empty space - } - } - } - - myFood.randomlyPlaceFood(); - - // setup event listeners - - myKeyListener = function(evt) { - if (!evt) var evt = window.event; - var keyNum = (evt.which) ? evt.which : evt.keyCode; - - if (me.getBoardState() === 1) { - if ( !(keyNum >= 37 && keyNum <= 40) ) {return;} // if not an arrow key, leave - - // This removes the listener added at the #listenerX line - SNAKE.removeEventListener(elmContainer, "keydown", myKeyListener, false); - - myKeyListener = function(evt) { - if (!evt) var evt = window.event; - var keyNum = (evt.which) ? evt.which : evt.keyCode; - - mySnake.handleArrowKeys(keyNum); - - evt.cancelBubble = true; - if (evt.stopPropagation) {evt.stopPropagation();} - if (evt.preventDefault) {evt.preventDefault();} - return false; - }; - SNAKE.addEventListener( elmContainer, "keydown", myKeyListener, false); - - mySnake.rebirth(); - mySnake.handleArrowKeys(keyNum); - me.setBoardState(2); // start the game! - mySnake.go(); - } - - evt.cancelBubble = true; - if (evt.stopPropagation) {evt.stopPropagation();} - if (evt.preventDefault) {evt.preventDefault();} - return false; - }; - - // Search for #listenerX to see where this is removed - SNAKE.addEventListener( elmContainer, "keydown", myKeyListener, false); - }; - - /** - * This method is called when the snake has eaten some food. - * @method foodEaten - */ - me.foodEaten = function() { - elmLengthPanel.innerHTML = "Length: " + mySnake.snakeLength; - myFood.randomlyPlaceFood(); - }; - - /** - * This method is called when the snake dies. - * @method handleDeath - */ - me.handleDeath = function() { - var index = Math.max(getNextHighestZIndex( mySnake.snakeBody), getNextHighestZIndex( {tmp:{elm:myFood.getFoodElement()}} )); - elmContainer.removeChild(elmTryAgain); - elmContainer.appendChild(elmTryAgain); - elmTryAgain.style.zIndex = index; - elmTryAgain.style.display = "block"; - me.setBoardState(0); - }; - - // --------------------------------------------------------------------- - // Initialize - // --------------------------------------------------------------------- - - config.fullScreen = (typeof config.fullScreen === "undefined") ? false : config.fullScreen; - config.top = (typeof config.top === "undefined") ? 0 : config.top; - config.left = (typeof config.left === "undefined") ? 0 : config.left; - config.width = (typeof config.width === "undefined") ? 400 : config.width; - config.height = (typeof config.height === "undefined") ? 400 : config.height; - - if (config.fullScreen) { - SNAKE.addEventListener(window,"resize", function() { - me.setupPlayingField(); - }, false); - } - - me.setBoardState(0); - - if (config.boardContainer) { - me.setBoardContainer(config.boardContainer); - } - - }; // end return function -})(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..d7f43490 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3389 @@ +{ + "name": "javascript-snake", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "javascript-snake", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "parcel": "^2.13.3", + "parcel-reporter-static-files-copy": "^1.5.3" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.26.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", + "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.25.9", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.25.9", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", + "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@lezer/common": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", + "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lezer/lr": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", + "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0" + } + }, + "node_modules/@lmdb/lmdb-darwin-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-2.8.5.tgz", + "integrity": "sha512-KPDeVScZgA1oq0CiPBcOa3kHIqU+pTOwRFDIhxvmf8CTNvqdZQYp5cCKW0bUk69VygB2PuTiINFWbY78aR2pQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-darwin-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-2.8.5.tgz", + "integrity": "sha512-w/sLhN4T7MW1nB3R/U8WK5BgQLz904wh+/SmA2jD8NnF7BLLoUgflCNxOeSPOWp8geP6nP/+VjWzZVip7rZ1ug==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-2.8.5.tgz", + "integrity": "sha512-c0TGMbm2M55pwTDIfkDLB6BpIsgxV4PjYck2HiOX+cy/JWiBXz32lYbarPqejKs9Flm7YVAKSILUducU9g2RVg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-arm64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-2.8.5.tgz", + "integrity": "sha512-vtbZRHH5UDlL01TT5jB576Zox3+hdyogvpcbvVJlmU5PdL3c5V7cj1EODdh1CHPksRl+cws/58ugEHi8bcj4Ww==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-linux-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-2.8.5.tgz", + "integrity": "sha512-Xkc8IUx9aEhP0zvgeKy7IQ3ReX2N8N1L0WPcQwnZweWmOuKfwpS3GRIYqLtK5za/w3E60zhFfNdS+3pBZPytqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@lmdb/lmdb-win32-x64": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-2.8.5.tgz", + "integrity": "sha512-4wvrf5BgnR8RpogHhtpCPJMKBmvyZPhhUtEwMJbXh0ni2BucpfF07jlmyM11zRqQ2XIq6PbC2j7W7UCCcm1rRQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@mischnic/json-sourcemap": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@mischnic/json-sourcemap/-/json-sourcemap-0.1.1.tgz", + "integrity": "sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@lezer/common": "^1.0.0", + "@lezer/lr": "^1.0.0", + "json5": "^2.2.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", + "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", + "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", + "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", + "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", + "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", + "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@parcel/bundler-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/bundler-default/-/bundler-default-2.13.3.tgz", + "integrity": "sha512-mOuWeth0bZzRv1b9Lrvydis/hAzJyePy0gwa0tix3/zyYBvw0JY+xkXVR4qKyD/blc1Ra2qOlfI2uD3ucnsdXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/graph": "3.3.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/cache": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/cache/-/cache-2.13.3.tgz", + "integrity": "sha512-Vz5+K5uCt9mcuQAMDo0JdbPYDmVdB8Nvu/A2vTEK2rqZPxvoOTczKeMBA4JqzKqGURHPRLaJCvuR8nDG+jhK9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/utils": "2.13.3", + "lmdb": "2.8.5" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/codeframe": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/codeframe/-/codeframe-2.13.3.tgz", + "integrity": "sha512-L/PQf+PT0xM8k9nc0B+PxxOYO2phQYnbuifu9o4pFRiqVmCtHztP+XMIvRJ2gOEXy3pgAImSPFVJ3xGxMFky4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/compressor-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/compressor-raw/-/compressor-raw-2.13.3.tgz", + "integrity": "sha512-C6vjDlgTLjYc358i7LA/dqcL0XDQZ1IHXFw6hBaHHOfxPKW2T4bzUI6RURyToEK9Q1X7+ggDKqgdLxwp4veCFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/config-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/config-default/-/config-default-2.13.3.tgz", + "integrity": "sha512-WUsx83ic8DgLwwnL1Bua4lRgQqYjxiTT+DBxESGk1paNm1juWzyfPXEQDLXwiCTcWMQGiXQFQ8OuSISauVQ8dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/bundler-default": "2.13.3", + "@parcel/compressor-raw": "2.13.3", + "@parcel/namer-default": "2.13.3", + "@parcel/optimizer-css": "2.13.3", + "@parcel/optimizer-htmlnano": "2.13.3", + "@parcel/optimizer-image": "2.13.3", + "@parcel/optimizer-svgo": "2.13.3", + "@parcel/optimizer-swc": "2.13.3", + "@parcel/packager-css": "2.13.3", + "@parcel/packager-html": "2.13.3", + "@parcel/packager-js": "2.13.3", + "@parcel/packager-raw": "2.13.3", + "@parcel/packager-svg": "2.13.3", + "@parcel/packager-wasm": "2.13.3", + "@parcel/reporter-dev-server": "2.13.3", + "@parcel/resolver-default": "2.13.3", + "@parcel/runtime-browser-hmr": "2.13.3", + "@parcel/runtime-js": "2.13.3", + "@parcel/runtime-react-refresh": "2.13.3", + "@parcel/runtime-service-worker": "2.13.3", + "@parcel/transformer-babel": "2.13.3", + "@parcel/transformer-css": "2.13.3", + "@parcel/transformer-html": "2.13.3", + "@parcel/transformer-image": "2.13.3", + "@parcel/transformer-js": "2.13.3", + "@parcel/transformer-json": "2.13.3", + "@parcel/transformer-postcss": "2.13.3", + "@parcel/transformer-posthtml": "2.13.3", + "@parcel/transformer-raw": "2.13.3", + "@parcel/transformer-react-refresh-wrap": "2.13.3", + "@parcel/transformer-svg": "2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/core": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/core/-/core-2.13.3.tgz", + "integrity": "sha512-SRZFtqGiaKHlZ2YAvf+NHvBFWS3GnkBvJMfOJM7kxJRK3M1bhbwJa/GgSdzqro5UVf9Bfj6E+pkdrRQIOZ7jMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/cache": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/graph": "3.3.3", + "@parcel/logger": "2.13.3", + "@parcel/package-manager": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/profiler": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "base-x": "^3.0.8", + "browserslist": "^4.6.6", + "clone": "^2.1.1", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "json5": "^2.2.0", + "msgpackr": "^1.9.9", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/diagnostic": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/diagnostic/-/diagnostic-2.13.3.tgz", + "integrity": "sha512-C70KXLBaXLJvr7XCEVu8m6TqNdw1gQLxqg5BQ8roR62R4vWWDnOq8PEksxDi4Y8Z/FF4i3Sapv6tRx9iBNxDEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/events": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/events/-/events-2.13.3.tgz", + "integrity": "sha512-ZkSHTTbD/E+53AjUzhAWTnMLnxLEU5yRw0H614CaruGh+GjgOIKyukGeToF5Gf/lvZ159VrJCGE0Z5EpgHVkuQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/feature-flags": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/feature-flags/-/feature-flags-2.13.3.tgz", + "integrity": "sha512-UZm14QpamDFoUut9YtCZSpG1HxPs07lUwUCpsAYL0PpxASD3oWJQxIJGfDZPa2272DarXDG9adTKrNXvkHZblw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/fs": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/fs/-/fs-2.13.3.tgz", + "integrity": "sha512-+MPWAt0zr+TCDSlj1LvkORTjfB/BSffsE99A9AvScKytDSYYpY2s0t4vtV9unSh0FHMS2aBCZNJ4t7KL+DcPIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/feature-flags": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/types-internal": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/watcher": "^2.0.7", + "@parcel/workers": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/graph": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@parcel/graph/-/graph-3.3.3.tgz", + "integrity": "sha512-pxs4GauEdvCN8nRd6wG3st6LvpHske3GfqGwUSR0P0X0pBPI1/NicvXz6xzp3rgb9gPWfbKXeI/2IOTfIxxVfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/feature-flags": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/logger": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/logger/-/logger-2.13.3.tgz", + "integrity": "sha512-8YF/ZhsQgd7ohQ2vEqcMD1Ag9JlJULROWRPGgGYLGD+twuxAiSdiFBpN3f+j4gQN4PYaLaIS/SwUFx11J243fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/markdown-ansi": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/markdown-ansi/-/markdown-ansi-2.13.3.tgz", + "integrity": "sha512-B4rUdlNUulJs2xOQuDbN7Hq5a9roq8IZUcJ1vQ8PAv+zMGb7KCfqIIr/BSCDYGhayfAGBVWW8x55Kvrl1zrDYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/namer-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/namer-default/-/namer-default-2.13.3.tgz", + "integrity": "sha512-A2a5A5fuyNcjSGOS0hPcdQmOE2kszZnLIXof7UMGNkNkeC62KAG8WcFZH5RNOY3LT5H773hq51zmc2Y2gE5Rnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/node-resolver-core": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/@parcel/node-resolver-core/-/node-resolver-core-3.4.3.tgz", + "integrity": "sha512-IEnMks49egEic1ITBp59VQyHzkSQUXqpU9hOHwqN3KoSTdZ6rEgrXcS3pa6tdXay4NYGlcZ88kFCE8i/xYoVCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@mischnic/json-sourcemap": "^0.1.0", + "@parcel/diagnostic": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-css/-/optimizer-css-2.13.3.tgz", + "integrity": "sha512-A8o9IVCv919vhv69SkLmyW2WjJR5WZgcMqV6L1uiGF8i8z18myrMhrp2JuSHx29PRT9uNyzNC4Xrd4StYjIhJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-htmlnano": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-htmlnano/-/optimizer-htmlnano-2.13.3.tgz", + "integrity": "sha512-K4Uvg0Sy2pECP7pdvvbud++F0pfcbNkq+IxTrgqBX5HJnLEmRZwgdvZEKF43oMEolclMnURMQRGjRplRaPdbXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "htmlnano": "^2.0.0", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-image": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-image/-/optimizer-image-2.13.3.tgz", + "integrity": "sha512-wlDUICA29J4UnqkKrWiyt68g1e85qfYhp4zJFcFJL0LX1qqh1QwsLUz3YJ+KlruoqPxJSFEC8ncBEKiVCsqhEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/optimizer-svgo": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-svgo/-/optimizer-svgo-2.13.3.tgz", + "integrity": "sha512-piIKxQKzhZK54dJR6yqIcq+urZmpsfgUpLCZT3cnWlX4ux5+S2iN66qqZBs0zVn+a58LcWcoP4Z9ieiJmpiu2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/optimizer-swc": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/optimizer-swc/-/optimizer-swc-2.13.3.tgz", + "integrity": "sha512-zNSq6oWqLlW8ksPIDjM0VgrK6ZAJbPQCDvs1V+p0oX3CzEe85lT5VkRpnfrN1+/vvEJNGL8e60efHKpI+rXGTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "@swc/core": "^1.7.26", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/package-manager": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/package-manager/-/package-manager-2.13.3.tgz", + "integrity": "sha512-FLNI5OrZxymGf/Yln0E/kjnGn5sdkQAxW7pQVdtuM+5VeN75yibJRjsSGv88PvJ+KvpD2ANgiIJo1RufmoPcww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/node-resolver-core": "3.4.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "@swc/core": "^1.7.26", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/packager-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-css/-/packager-css-2.13.3.tgz", + "integrity": "sha512-ghDqRMtrUwaDERzFm9le0uz2PTeqqsjsW0ihQSZPSAptElRl9o5BR+XtMPv3r7Ui0evo+w35gD55oQCJ28vCig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-html": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-html/-/packager-html-2.13.3.tgz", + "integrity": "sha512-jDLnKSA/EzVEZ3/aegXO3QJ/Ij732AgBBkIQfeC8tUoxwVz5b3HiPBAjVjcUSfZs7mdBSHO+ELWC3UD+HbsIrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-js/-/packager-js-2.13.3.tgz", + "integrity": "sha512-0pMHHf2zOn7EOJe88QJw5h/wcV1bFfj6cXVcE55Wa8GX3V+SdCgolnlvNuBcRQ1Tlx0Xkpo+9hMFVIQbNQY6zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "globals": "^13.2.0", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-raw/-/packager-raw-2.13.3.tgz", + "integrity": "sha512-AWu4UB+akBdskzvT3KGVHIdacU9f7cI678DQQ1jKQuc9yZz5D0VFt3ocFBOmvDfEQDF0uH3jjtJR7fnuvX7Biw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-svg": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-svg/-/packager-svg-2.13.3.tgz", + "integrity": "sha512-tKGRiFq/4jh5u2xpTstNQ7gu+RuZWzlWqpw5NaFmcKe6VQe5CMcS499xTFoREAGnRvevSeIgC38X1a+VOo+/AA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "posthtml": "^0.16.4" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/packager-wasm": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/packager-wasm/-/packager-wasm-2.13.3.tgz", + "integrity": "sha512-SZB56/b230vFrSehVXaUAWjJmWYc89gzb8OTLkBm7uvtFtov2J1R8Ig9TTJwinyXE3h84MCFP/YpQElSfoLkJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">=16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/plugin": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/plugin/-/plugin-2.13.3.tgz", + "integrity": "sha512-cterKHHcwg6q11Gpif/aqvHo056TR+yDVJ3fSdiG2xr5KD1VZ2B3hmofWERNNwjMcnR1h9Xq40B7jCKUhOyNFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/types": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/profiler": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/profiler/-/profiler-2.13.3.tgz", + "integrity": "sha512-ok6BwWSLvyHe5TuSXjSacYnDStFgP5Y30tA9mbtWSm0INDsYf+m5DqzpYPx8U54OaywWMK8w3MXUClosJX3aPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/types-internal": "2.13.3", + "chrome-trace-event": "^1.0.2" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-cli": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-cli/-/reporter-cli-2.13.3.tgz", + "integrity": "sha512-EA5tKt/6bXYNMEavSs35qHlFdx6cZmRazlZxPBgxPePQYoouNAPMNLUOEQozaPhz9f5fvNDN7EHOFaAWcdO2LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/types": "2.13.3", + "@parcel/utils": "2.13.3", + "chalk": "^4.1.2", + "term-size": "^2.2.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-dev-server": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-dev-server/-/reporter-dev-server-2.13.3.tgz", + "integrity": "sha512-ZNeFp6AOIQFv7mZIv2P5O188dnZHNg0ymeDVcakfZomwhpSva2dFNS3AnvWo4eyWBlUxkmQO8BtaxeWTs7jAuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/reporter-tracer": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/reporter-tracer/-/reporter-tracer-2.13.3.tgz", + "integrity": "sha512-aBsVPI8jLZTDkFYrI69GxnsdvZKEYerkPsu935LcX9rfUYssOnmmUP+3oI+8fbg+qNjJuk9BgoQ4hCp9FOphMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "chrome-trace-event": "^1.0.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/resolver-default": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/resolver-default/-/resolver-default-2.13.3.tgz", + "integrity": "sha512-urBZuRALWT9pFMeWQ8JirchLmsQEyI9lrJptiwLbJWrwvmlwSUGkcstmPwoNRf/aAQjICB7ser/247Vny0pFxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/node-resolver-core": "3.4.3", + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-browser-hmr": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-browser-hmr/-/runtime-browser-hmr-2.13.3.tgz", + "integrity": "sha512-EAcPojQFUNUGUrDk66cu3ySPO0NXRVS5CKPd4QrxPCVVbGzde4koKu8krC/TaGsoyUqhie8HMnS70qBP0GFfcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-js/-/runtime-js-2.13.3.tgz", + "integrity": "sha512-62OucNAnxb2Q0uyTFWW/0Hvv2DJ4b5H6neh/YFu2/wmxaZ37xTpEuEcG2do7KW54xE5DeLP+RliHLwi4NvR3ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-react-refresh": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-react-refresh/-/runtime-react-refresh-2.13.3.tgz", + "integrity": "sha512-PYZ1klpJVwqE3WuifILjtF1dugtesHEuJcXYZI85T6UoRSD5ctS1nAIpZzT14Ga1lRt/jd+eAmhWL1l3m/Vk1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "react-error-overlay": "6.0.9", + "react-refresh": ">=0.9 <=0.14" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/runtime-service-worker": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/runtime-service-worker/-/runtime-service-worker-2.13.3.tgz", + "integrity": "sha512-BjMhPuT7Us1+YIo31exPRwomPiL+jrZZS5UUAwlEW2XGHDceEotzRM94LwxeFliCScT4IOokGoxixm19qRuzWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/rust": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/rust/-/rust-2.13.3.tgz", + "integrity": "sha512-dLq85xDAtzr3P5200cvxk+8WXSWauYbxuev9LCPdwfhlaWo/JEj6cu9seVdWlkagjGwkoV1kXC+GGntgUXOLAQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/source-map": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@parcel/source-map/-/source-map-2.1.1.tgz", + "integrity": "sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": "^12.18.3 || >=14" + } + }, + "node_modules/@parcel/transformer-babel": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-babel/-/transformer-babel-2.13.3.tgz", + "integrity": "sha512-ikzK9f5WTFrdQsPitQgjCPH6HmVU8AQPRemIJ2BndYhtodn5PQut5cnSvTrqax8RjYvheEKCQk/Zb/uR7qgS3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "json5": "^2.2.0", + "nullthrows": "^1.1.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-css": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-css/-/transformer-css-2.13.3.tgz", + "integrity": "sha512-zbrNURGph6JeVADbGydyZ7lcu/izj41kDxQ9xw4RPRW/3rofQiTU0OTREi+uBWiMENQySXVivEdzHA9cA+aLAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "browserslist": "^4.6.6", + "lightningcss": "^1.22.1", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-html/-/transformer-html-2.13.3.tgz", + "integrity": "sha512-Yf74FkL9RCCB4+hxQRVMNQThH9+fZ5w0NLiQPpWUOcgDEEyxTi4FWPQgEBsKl/XK2ehdydbQB9fBgPQLuQxwPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2", + "srcset": "4" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-html/node_modules/srcset": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-4.0.0.tgz", + "integrity": "sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@parcel/transformer-image": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-image/-/transformer-image-2.13.3.tgz", + "integrity": "sha512-wL1CXyeFAqbp2wcEq/JD3a/tbAyVIDMTC6laQxlIwnVV7dsENhK1qRuJZuoBdixESeUpFQSmmQvDIhcfT/cUUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/transformer-js": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-js/-/transformer-js-2.13.3.tgz", + "integrity": "sha512-KqfNGn1IHzDoN2aPqt4nDksgb50Xzcny777C7A7hjlQ3cmkjyJrixYjzzsPaPSGJ+kJpknh3KE8unkQ9mhFvRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "@parcel/utils": "2.13.3", + "@parcel/workers": "2.13.3", + "@swc/helpers": "^0.5.0", + "browserslist": "^4.6.6", + "nullthrows": "^1.1.1", + "regenerator-runtime": "^0.14.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@parcel/transformer-json": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-json/-/transformer-json-2.13.3.tgz", + "integrity": "sha512-rrq0ab6J0w9ePtsxi0kAvpCmrUYXXAx1Z5PATZakv89rSYbHBKEdXxyCoKFui/UPVCUEGVs5r0iOFepdHpIyeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "json5": "^2.2.0" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-postcss": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-postcss/-/transformer-postcss-2.13.3.tgz", + "integrity": "sha512-AIiWpU0QSFBrPcYIqAnhqB8RGE6yHFznnxztfg1t2zMSOnK3xoU6xqYKv8H/MduShGGrC3qVOeDfM8MUwzL3cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/utils": "2.13.3", + "clone": "^2.1.1", + "nullthrows": "^1.1.1", + "postcss-value-parser": "^4.2.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-posthtml": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-posthtml/-/transformer-posthtml-2.13.3.tgz", + "integrity": "sha512-5GSLyccpHASwFAu3uJ83gDIBSvfsGdVmhJvy0Vxe+K1Fklk2ibhvvtUHMhB7mg6SPHC+R9jsNc3ZqY04ZLeGjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-raw": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-raw/-/transformer-raw-2.13.3.tgz", + "integrity": "sha512-BFsAbdQF0l8/Pdb7dSLJeYcd8jgwvAUbHgMink2MNXJuRUvDl19Gns8jVokU+uraFHulJMBj40+K/RTd33in4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-react-refresh-wrap": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-react-refresh-wrap/-/transformer-react-refresh-wrap-2.13.3.tgz", + "integrity": "sha512-mOof4cRyxsZRdg8kkWaFtaX98mHpxUhcGPU+nF9RQVa9q737ItxrorsPNR9hpZAyE2TtFNflNW7RoYsgvlLw8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "2.13.3", + "@parcel/utils": "2.13.3", + "react-refresh": ">=0.9 <=0.14" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/transformer-svg": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/transformer-svg/-/transformer-svg-2.13.3.tgz", + "integrity": "sha512-9jm7ZF4KHIrGLWlw/SFUz5KKJ20nxHvjFAmzde34R9Wu+F1BOjLZxae7w4ZRwvIc+UVOUcBBQFmhSVwVDZg6Dw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/plugin": "2.13.3", + "@parcel/rust": "2.13.3", + "nullthrows": "^1.1.1", + "posthtml": "^0.16.5", + "posthtml-parser": "^0.12.1", + "posthtml-render": "^3.0.0", + "semver": "^7.5.2" + }, + "engines": { + "node": ">= 16.0.0", + "parcel": "^2.13.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/types": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/types/-/types-2.13.3.tgz", + "integrity": "sha512-+RpFHxx8fy8/dpuehHUw/ja9PRExC3wJoIlIIF42E7SLu2SvlTHtKm6EfICZzxCXNEBzjoDbamCRcN0nmTPlhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/types-internal": "2.13.3", + "@parcel/workers": "2.13.3" + } + }, + "node_modules/@parcel/types-internal": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/types-internal/-/types-internal-2.13.3.tgz", + "integrity": "sha512-Lhx0n+9RCp+Ipktf/I+CLm3zE9Iq9NtDd8b2Vr5lVWyoT8AbzBKIHIpTbhLS4kjZ80L3I6o93OYjqAaIjsqoZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/source-map": "^2.1.1", + "utility-types": "^3.10.0" + } + }, + "node_modules/@parcel/utils": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/utils/-/utils-2.13.3.tgz", + "integrity": "sha512-yxY9xw2wOUlJaScOXYZmMGoZ4Ck4Kqj+p6Koe5kLkkWM1j98Q0Dj2tf/mNvZi4yrdnlm+dclCwNRnuE8Q9D+pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/codeframe": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/markdown-ansi": "2.13.3", + "@parcel/rust": "2.13.3", + "@parcel/source-map": "^2.1.1", + "chalk": "^4.1.2", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", + "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^1.0.3", + "is-glob": "^4.0.3", + "micromatch": "^4.0.5", + "node-addon-api": "^7.0.0" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.1", + "@parcel/watcher-darwin-arm64": "2.5.1", + "@parcel/watcher-darwin-x64": "2.5.1", + "@parcel/watcher-freebsd-x64": "2.5.1", + "@parcel/watcher-linux-arm-glibc": "2.5.1", + "@parcel/watcher-linux-arm-musl": "2.5.1", + "@parcel/watcher-linux-arm64-glibc": "2.5.1", + "@parcel/watcher-linux-arm64-musl": "2.5.1", + "@parcel/watcher-linux-x64-glibc": "2.5.1", + "@parcel/watcher-linux-x64-musl": "2.5.1", + "@parcel/watcher-win32-arm64": "2.5.1", + "@parcel/watcher-win32-ia32": "2.5.1", + "@parcel/watcher-win32-x64": "2.5.1" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", + "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", + "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", + "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", + "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", + "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", + "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", + "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", + "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", + "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", + "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", + "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", + "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", + "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/workers": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/@parcel/workers/-/workers-2.13.3.tgz", + "integrity": "sha512-oAHmdniWTRwwwsKbcF4t3VjOtKN+/W17Wj5laiYB+HLkfsjGTfIQPj3sdXmrlBAGpI4omIcvR70PHHXnfdTfwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/diagnostic": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/profiler": "2.13.3", + "@parcel/types-internal": "2.13.3", + "@parcel/utils": "2.13.3", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "peerDependencies": { + "@parcel/core": "^2.13.3" + } + }, + "node_modules/@swc/core": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.12.tgz", + "integrity": "sha512-+iUL0PYpPm6N9AdV1wvafakvCqFegQus1aoEDxgFsv3/uNVNIyRaupf/v/Zkp5hbep2EzhtoJR0aiJIzDbXWHg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.17" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.10.12", + "@swc/core-darwin-x64": "1.10.12", + "@swc/core-linux-arm-gnueabihf": "1.10.12", + "@swc/core-linux-arm64-gnu": "1.10.12", + "@swc/core-linux-arm64-musl": "1.10.12", + "@swc/core-linux-x64-gnu": "1.10.12", + "@swc/core-linux-x64-musl": "1.10.12", + "@swc/core-win32-arm64-msvc": "1.10.12", + "@swc/core-win32-ia32-msvc": "1.10.12", + "@swc/core-win32-x64-msvc": "1.10.12" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.12.tgz", + "integrity": "sha512-pOANQegUTAriW7jq3SSMZGM5l89yLVMs48R0F2UG6UZsH04SiViCnDctOGlA/Sa++25C+rL9MGMYM1jDLylBbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.12.tgz", + "integrity": "sha512-m4kbpIDDsN1FrwfNQMU+FTrss356xsXvatLbearwR+V0lqOkjLBP0VmRvQfHEg+uy13VPyrT9gj4HLoztlci7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.12.tgz", + "integrity": "sha512-OY9LcupgqEu8zVK+rJPes6LDJJwPDmwaShU96beTaxX2K6VrXbpwm5WbPS/8FfQTsmpnuA7dCcMPUKhNgmzTrQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.12.tgz", + "integrity": "sha512-nJD587rO0N4y4VZszz3xzVr7JIiCzSMhEMWnPjuh+xmPxDBz0Qccpr8xCr1cSxpl1uY7ERkqAGlKr6CwoV5kVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.12.tgz", + "integrity": "sha512-oqhSmV+XauSf0C//MoQnVErNUB/5OzmSiUzuazyLsD5pwqKNN+leC3JtRQ/QVzaCpr65jv9bKexT9+I2Tt3xDw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.12.tgz", + "integrity": "sha512-XldSIHyjD7m1Gh+/8rxV3Ok711ENLI420CU2EGEqSe3VSGZ7pHJvJn9ZFbYpWhsLxPqBYMFjp3Qw+J6OXCPXCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.12.tgz", + "integrity": "sha512-wvPXzJxzPgTqhyp1UskOx1hRTtdWxlyFD1cGWOxgLsMik0V9xKRgqKnMPv16Nk7L9xl6quQ6DuUHj9ID7L3oVw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.12.tgz", + "integrity": "sha512-TUYzWuu1O7uyIcRfxdm6Wh1u+gNnrW5M1DUgDOGZLsyQzgc2Zjwfh2llLhuAIilvCVg5QiGbJlpibRYJ/8QGsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.12.tgz", + "integrity": "sha512-4Qrw+0Xt+Fe2rz4OJ/dEPMeUf/rtuFWWAj/e0vL7J5laUHirzxawLRE5DCJLQTarOiYR6mWnmadt9o3EKzV6Xg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.10.12", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.12.tgz", + "integrity": "sha512-YiloZXLW7rUxJpALwHXaGjVaAEn+ChoblG7/3esque+Y7QCyheoBUJp2DVM1EeVA43jBfZ8tvYF0liWd9Tpz1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@swc/types": { + "version": "0.1.17", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz", + "integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "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, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/base-x": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.10.tgz", + "integrity": "sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001696", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001696.tgz", + "integrity": "sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "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, + "license": "MIT", + "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/chrome-trace-event": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", + "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "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, + "license": "MIT", + "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, + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/cosmiconfig": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", + "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.1", + "import-fresh": "^3.3.0", + "js-yaml": "^4.1.0", + "parse-json": "^5.2.0" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/d-fischer" + }, + "peerDependencies": { + "typescript": ">=4.9.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "detect-libc": "bin/detect-libc.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dotenv": { + "version": "16.4.7", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", + "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.90", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.90.tgz", + "integrity": "sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/get-port": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-4.2.0.tgz", + "integrity": "sha512-/b3jarXkH8KJoOMQc3uVGHASwGLPq3gSFJ7tgJm2diza+bydJPTGOibin2steecKeOylE8oY2JERlVWkAJO6yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/htmlnano": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/htmlnano/-/htmlnano-2.1.1.tgz", + "integrity": "sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cosmiconfig": "^9.0.0", + "posthtml": "^0.16.5", + "timsort": "^0.3.0" + }, + "peerDependencies": { + "cssnano": "^7.0.0", + "postcss": "^8.3.11", + "purgecss": "^6.0.0", + "relateurl": "^0.2.7", + "srcset": "5.0.1", + "svgo": "^3.0.2", + "terser": "^5.10.0", + "uncss": "^0.17.3" + }, + "peerDependenciesMeta": { + "cssnano": { + "optional": true + }, + "postcss": { + "optional": true + }, + "purgecss": { + "optional": true + }, + "relateurl": { + "optional": true + }, + "srcset": { + "optional": true + }, + "svgo": { + "optional": true + }, + "terser": { + "optional": true + }, + "uncss": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", + "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT" + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-json": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-json/-/is-json-2.0.1.tgz", + "integrity": "sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==", + "dev": true, + "license": "ISC" + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "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, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.1.tgz", + "integrity": "sha512-FmGoeD4S05ewj+AkhTY+D+myDvXI6eL27FjHIjoyUkO/uw7WZD1fBVs0QxeYWa7E17CUHJaYX/RUGISCtcrG4Q==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^1.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.1", + "lightningcss-darwin-x64": "1.29.1", + "lightningcss-freebsd-x64": "1.29.1", + "lightningcss-linux-arm-gnueabihf": "1.29.1", + "lightningcss-linux-arm64-gnu": "1.29.1", + "lightningcss-linux-arm64-musl": "1.29.1", + "lightningcss-linux-x64-gnu": "1.29.1", + "lightningcss-linux-x64-musl": "1.29.1", + "lightningcss-win32-arm64-msvc": "1.29.1", + "lightningcss-win32-x64-msvc": "1.29.1" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.1.tgz", + "integrity": "sha512-HtR5XJ5A0lvCqYAoSv2QdZZyoHNttBpa5EP9aNuzBQeKGfbyH5+UipLWvVzpP4Uml5ej4BYs5I9Lco9u1fECqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.1.tgz", + "integrity": "sha512-k33G9IzKUpHy/J/3+9MCO4e+PzaFblsgBjSGlpAaFikeBFm8B/CkO3cKU9oI4g+fjS2KlkLM/Bza9K/aw8wsNA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.1.tgz", + "integrity": "sha512-0SUW22fv/8kln2LnIdOCmSuXnxgxVC276W5KLTwoehiO0hxkacBxjHOL5EtHD8BAXg2BvuhsJPmVMasvby3LiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.1.tgz", + "integrity": "sha512-sD32pFvlR0kDlqsOZmYqH/68SqUMPNj+0pucGxToXZi4XZgZmqeX/NkxNKCPsswAXU3UeYgDSpGhu05eAufjDg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.1.tgz", + "integrity": "sha512-0+vClRIZ6mmJl/dxGuRsE197o1HDEeeRk6nzycSy2GofC2JsY4ifCRnvUWf/CUBQmlrvMzt6SMQNMSEu22csWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.1.tgz", + "integrity": "sha512-UKMFrG4rL/uHNgelBsDwJcBqVpzNJbzsKkbI3Ja5fg00sgQnHw/VrzUTEc4jhZ+AN2BvQYz/tkHu4vt1kLuJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.1.tgz", + "integrity": "sha512-u1S+xdODy/eEtjADqirA774y3jLcm8RPtYztwReEXoZKdzgsHYPl0s5V52Tst+GKzqjebkULT86XMSxejzfISw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.1.tgz", + "integrity": "sha512-L0Tx0DtaNUTzXv0lbGCLB/c/qEADanHbu4QdcNOXLIe1i8i22rZRpbT3gpWYsCh9aSL9zFujY/WmEXIatWvXbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.1.tgz", + "integrity": "sha512-QoOVnkIEFfbW4xPi+dpdft/zAKmgLgsRHfJalEPYuJDOWf7cLQzYg0DEh8/sn737FaeMJxHZRc1oBreiwZCjog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.1.tgz", + "integrity": "sha512-NygcbThNBe4JElP+olyTI/doBNGJvLs3bFCRPdvuCcxZCcCZ71B858IHpdm7L1btZex0FvCmM17FK98Y9MRy1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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, + "license": "MIT" + }, + "node_modules/lmdb": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-2.8.5.tgz", + "integrity": "sha512-9bMdFfc80S+vSldBmG3HOuLVHnxRdNTlpzR6QDnzqCQtCzGUEAGTzBKYMeIM+I/sU4oZfgbcbS7X7F65/z/oxQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "msgpackr": "^1.9.5", + "node-addon-api": "^6.1.0", + "node-gyp-build-optional-packages": "5.1.1", + "ordered-binary": "^1.4.1", + "weak-lru-cache": "^1.2.2" + }, + "bin": { + "download-lmdb-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@lmdb/lmdb-darwin-arm64": "2.8.5", + "@lmdb/lmdb-darwin-x64": "2.8.5", + "@lmdb/lmdb-linux-arm": "2.8.5", + "@lmdb/lmdb-linux-arm64": "2.8.5", + "@lmdb/lmdb-linux-x64": "2.8.5", + "@lmdb/lmdb-win32-x64": "2.8.5" + } + }, + "node_modules/lmdb/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/msgpackr": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.2.tgz", + "integrity": "sha512-F9UngXRlPyWCDEASDpTf6c9uNhGPTqnTeLVt7bN+bU1eajoR/8V9ys2BRaV5C/e5ihE6sJ9uPIKaYt6bFuO32g==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "msgpackr-extract": "^3.0.2" + } + }, + "node_modules/msgpackr-extract": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", + "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-gyp-build-optional-packages": "5.2.2" + }, + "bin": { + "download-msgpackr-prebuilds": "bin/download-prebuilds.js" + }, + "optionalDependencies": { + "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", + "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" + } + }, + "node_modules/msgpackr-extract/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/msgpackr-extract/node_modules/node-gyp-build-optional-packages": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", + "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-gyp-build-optional-packages": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.1.1.tgz", + "integrity": "sha512-+P72GAjVAbTxjjwUmwjVrqrdZROD4nf8KgpBoDxqXXTiYZZt/ud60dE5yvCSr9lRO8e8yv6kgJIC0K0PfZFVQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.1" + }, + "bin": { + "node-gyp-build-optional-packages": "bin.js", + "node-gyp-build-optional-packages-optional": "optional.js", + "node-gyp-build-optional-packages-test": "build-test.js" + } + }, + "node_modules/node-gyp-build-optional-packages/node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ordered-binary": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", + "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parcel": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/parcel/-/parcel-2.13.3.tgz", + "integrity": "sha512-8GrC8C7J8mwRpAlk7EJ7lwdFTbCN+dcXH2gy5AsEs9pLfzo9wvxOTx6W0fzSlvCOvZOita+8GdfYlGfEt0tRgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/config-default": "2.13.3", + "@parcel/core": "2.13.3", + "@parcel/diagnostic": "2.13.3", + "@parcel/events": "2.13.3", + "@parcel/feature-flags": "2.13.3", + "@parcel/fs": "2.13.3", + "@parcel/logger": "2.13.3", + "@parcel/package-manager": "2.13.3", + "@parcel/reporter-cli": "2.13.3", + "@parcel/reporter-dev-server": "2.13.3", + "@parcel/reporter-tracer": "2.13.3", + "@parcel/utils": "2.13.3", + "chalk": "^4.1.2", + "commander": "^12.1.0", + "get-port": "^4.2.0" + }, + "bin": { + "parcel": "lib/bin.js" + }, + "engines": { + "node": ">= 16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/parcel-reporter-static-files-copy": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/parcel-reporter-static-files-copy/-/parcel-reporter-static-files-copy-1.5.3.tgz", + "integrity": "sha512-Ukq2SyJYn3GFIPCLamXuQ+2t+0j54llujjOUoRjtmVvfsuGnJDEpMznADeIoKuQDvy0jpxtWzWkQvxqI/j+U4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@parcel/plugin": "^2.0.0-beta.1" + }, + "engines": { + "parcel": "^2.0.0-beta.1" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "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, + "license": "MIT", + "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/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/posthtml": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/posthtml/-/posthtml-0.16.6.tgz", + "integrity": "sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "posthtml-parser": "^0.11.0", + "posthtml-render": "^3.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/posthtml-parser": { + "version": "0.12.1", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.12.1.tgz", + "integrity": "sha512-rYFmsDLfYm+4Ts2Oh4DCDSZPtdC1BLnRXAobypVzX9alj28KGl65dIFtgDY9zB57D0TC4Qxqrawuq/2et1P0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^9.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/posthtml-render": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/posthtml-render/-/posthtml-render-3.0.0.tgz", + "integrity": "sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-json": "^2.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/posthtml/node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/posthtml/node_modules/dom-serializer/node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml/node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/posthtml/node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/posthtml/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/posthtml/node_modules/htmlparser2": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-7.2.0.tgz", + "integrity": "sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.2", + "domutils": "^2.8.0", + "entities": "^3.0.1" + } + }, + "node_modules/posthtml/node_modules/posthtml-parser": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/posthtml-parser/-/posthtml-parser-0.11.0.tgz", + "integrity": "sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "htmlparser2": "^7.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-error-overlay": { + "version": "6.0.9", + "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.9.tgz", + "integrity": "sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==", + "dev": true, + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.0.tgz", + "integrity": "sha512-DrfFnPzblFmNrIZzg5RzHegbiRWg7KMR7btwi2yjHwx06zsUbO5g613sVwEV7FTwmzJu+Io0lJe2GJ3LxqpvBQ==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/srcset": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/srcset/-/srcset-5.0.1.tgz", + "integrity": "sha512-/P1UYbGfJVlxZag7aABNRrulEXAwCSDo7fklafOQrantuPTDmYgijJMks2zusPCVzgW9+4P69mq7w6pYuZpgxw==", + "dev": true, + "license": "MIT", + "optional": true, + "peer": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "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, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/term-size": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz", + "integrity": "sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/timsort": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz", + "integrity": "sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==", + "dev": true, + "license": "MIT" + }, + "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, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz", + "integrity": "sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/utility-types": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/utility-types/-/utility-types-3.11.0.tgz", + "integrity": "sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/weak-lru-cache": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/weak-lru-cache/-/weak-lru-cache-1.2.2.tgz", + "integrity": "sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==", + "dev": true, + "license": "MIT" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..6ff84628 --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "javascript-snake", + "version": "1.0.0", + "description": "JavaScript Snake
By Patrick Gillespie
License: MIT
http://patorjk.com/games/snake", + "scripts": { + "start": "parcel src/index.html --open", + "build": "parcel build src/index.html", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/patorjk/JavaScript-Snake.git" + }, + "author": "", + "license": "MIT", + "bugs": { + "url": "https://github.com/patorjk/JavaScript-Snake/issues" + }, + "homepage": "https://github.com/patorjk/JavaScript-Snake#readme", + "devDependencies": { + "parcel": "^2.13.3", + "parcel-reporter-static-files-copy": "^1.5.3" + }, + "staticFiles": { + "staticPath": "src/css", + "staticOutPath": "css" + } +} diff --git a/src/ai-example.html b/src/ai-example.html new file mode 100644 index 00000000..d3ca29d7 --- /dev/null +++ b/src/ai-example.html @@ -0,0 +1,148 @@ + + + + + + JavaScript Snake - AI Example + + + + + + + +
+
+ Theme: + +
+
+ Mode: + +
+ +
+
+ +
+ +
+ + + + + diff --git a/src/css/Senura-snake.css b/src/css/Senura-snake.css new file mode 100644 index 00000000..2102a783 --- /dev/null +++ b/src/css/Senura-snake.css @@ -0,0 +1,146 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ + +body { + margin: 0px; + padding: 0px; + background-color: #000000; +} + +.snake-toolbar { + color: #847a87; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#game-area:focus { + outline: none; +} + +#mode-wrapper { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #f6f0f7; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #605d61; +} + +a.snake-link:hover { + color: #a500d6; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: #ffffff; + color: #938996; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #cf6d6d; + text-align: center; + background-color: #550b70; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #eddff2; + position: absolute; + border: 0px solid black; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/dark-snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/dead-dark-snakeblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: black; + border: 2px solid #000000; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: #fcfcfc; + border: 3px solid black; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + background-color: #3e2e44; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: #ab00de; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: #ab00de; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/black-snake.css b/src/css/black-snake.css new file mode 100644 index 00000000..a92710b5 --- /dev/null +++ b/src/css/black-snake.css @@ -0,0 +1,142 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ +body { + margin: 0px; + padding: 0px; + background-color: black; + color: white; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} + +#game-area:focus { + outline: none; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #fcfc54; +} + +a.snake-link:hover { + color: #ffff54; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: black; + color: white; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #ffffff; + text-align: center; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/cssss/images/snakeblock.png"), + url("src/css/images/snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/cssss/images/deadblock.png"), + url("src/css/images/deadblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: aqua; + border: 0px solid #000080; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: purple; + border: 0px solid purple; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/dark-snake.css b/src/css/dark-snake.css new file mode 100644 index 00000000..16388e41 --- /dev/null +++ b/src/css/dark-snake.css @@ -0,0 +1,146 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ + +body { + margin: 0px; + padding: 0px; + background-color: #3e2e44; +} + +.snake-toolbar { + color: #938996; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#game-area:focus { + outline: none; +} + +#mode-wrapper { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #938996; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #938996; +} + +a.snake-link:hover { + color: #938996; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: #3e2e44; + color: #938996; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #938996; + text-align: center; + background-color: #3e2e44; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #3e2e44; + position: absolute; + border: 0px solid black; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/dark-snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/dead-dark-snakeblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: black; + border: 2px solid #3e2e44; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: #312e44; + border: 3px solid black; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + background-color: #3e2e44; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: #938996; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: #938996; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/green-snake.css b/src/css/green-snake.css new file mode 100644 index 00000000..9cd114c5 --- /dev/null +++ b/src/css/green-snake.css @@ -0,0 +1,147 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ +body { + margin: 0px; + padding: 0px; + background-color: darkgreen; +} + +.snake-toolbar { + background-color: rgba(255, 255, 255, 0.4); + border-radius: 10px; +} + +#game-area { + margin: 10px; + padding: 0px; + background-color: lightgreen; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} + +#game-area:focus { + outline: none; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: black; +} + +a.snake-link:hover { + color: #ffff54; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: black; + color: white; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: black; + text-align: center; + background-color: white; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/deadblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: black; + border: 0px solid #000080; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: white; + border: 0px solid #0000a8; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + background-color: #fc5454; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/head-snake.css b/src/css/head-snake.css new file mode 100644 index 00000000..0e7daf95 --- /dev/null +++ b/src/css/head-snake.css @@ -0,0 +1,148 @@ +body { + margin: 0px; + padding: 0px; + background-color: rgb(0, 0, 0); +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} + +#game-area:focus { + outline: none; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #fcfc54; +} + +a.snake-link:hover { + color: #ffff54; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: black; + color: white; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #ffffff; + text-align: center; + padding: 8px; + margin: 0px; +} + +#snake-snakehead-alive { + background-image: url("src/css/images/green-head-snakeblock.png"); + margin: 0px; + padding: 0px; + background-color: rgb(10, 173, 10); + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; + border-radius: 4px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/green-body-snakeblock.png"), + url("src/css/images/green-body-snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/deadblock.png"), + url("src/css/images/deadblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: rgb(182, 11, 11); + border: 0px solid #000080; + position: absolute; + border-radius: 6px; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: rgb(245, 245, 220); + border: 0px solid #0000a8; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/css/images/Thumbs.db b/src/css/images/Thumbs.db old mode 100644 new mode 100755 similarity index 100% rename from css/images/Thumbs.db rename to src/css/images/Thumbs.db diff --git a/src/css/images/dark-snakeblock.png b/src/css/images/dark-snakeblock.png new file mode 100644 index 00000000..66c4f1c2 Binary files /dev/null and b/src/css/images/dark-snakeblock.png differ diff --git a/src/css/images/dead-dark-snakeblock.png b/src/css/images/dead-dark-snakeblock.png new file mode 100644 index 00000000..63302e51 Binary files /dev/null and b/src/css/images/dead-dark-snakeblock.png differ diff --git a/css/images/deadblock.png b/src/css/images/deadblock.png old mode 100644 new mode 100755 similarity index 100% rename from css/images/deadblock.png rename to src/css/images/deadblock.png diff --git a/css/images/deadblock_border.png b/src/css/images/deadblock_border.png similarity index 100% rename from css/images/deadblock_border.png rename to src/css/images/deadblock_border.png diff --git a/src/css/images/favicon.png b/src/css/images/favicon.png new file mode 100644 index 00000000..ccbbc5d8 Binary files /dev/null and b/src/css/images/favicon.png differ diff --git a/src/css/images/green-body-snakeblock.png b/src/css/images/green-body-snakeblock.png new file mode 100644 index 00000000..fa9d23de Binary files /dev/null and b/src/css/images/green-body-snakeblock.png differ diff --git a/src/css/images/green-head-snakeblock.png b/src/css/images/green-head-snakeblock.png new file mode 100644 index 00000000..5d235cd5 Binary files /dev/null and b/src/css/images/green-head-snakeblock.png differ diff --git a/src/css/images/matrix-food-block.png b/src/css/images/matrix-food-block.png new file mode 100644 index 00000000..5e3ef68b Binary files /dev/null and b/src/css/images/matrix-food-block.png differ diff --git a/src/css/images/matrix-snake-block.png b/src/css/images/matrix-snake-block.png new file mode 100644 index 00000000..8416cc80 Binary files /dev/null and b/src/css/images/matrix-snake-block.png differ diff --git a/src/css/images/neon-body-snakeblock.png b/src/css/images/neon-body-snakeblock.png new file mode 100644 index 00000000..d66f7717 Binary files /dev/null and b/src/css/images/neon-body-snakeblock.png differ diff --git a/src/css/images/neon-dead-snakeblock.png b/src/css/images/neon-dead-snakeblock.png new file mode 100644 index 00000000..55da41ab Binary files /dev/null and b/src/css/images/neon-dead-snakeblock.png differ diff --git a/css/images/snakeblock.png b/src/css/images/snakeblock.png old mode 100644 new mode 100755 similarity index 100% rename from css/images/snakeblock.png rename to src/css/images/snakeblock.png diff --git a/src/css/light-snake.css b/src/css/light-snake.css new file mode 100644 index 00000000..db0be890 --- /dev/null +++ b/src/css/light-snake.css @@ -0,0 +1,127 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ + +body { + margin: 0px; + padding: 0px; + background-color: #f73378; +} +#game-area { + margin: 10px; + padding: 0px; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} +#game-area:focus { + outline: none; +} +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #fcfc54; +} +a.snake-link:hover { + color: #ffff54; +} +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: black; + color: white; +} +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #ffffff; + text-align: center; + padding: 8px; + margin: 0px; +} +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} +.snake-snakebody-alive { + background-image: url("src/css/images/snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/deadblock.png"); +} +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: #6cfd6a; + border: 0px solid #000080; + position: absolute; +} +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: #ab003c; + border: 0px solid #ab003c; +} +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + position: relative; +} +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/main-snake.css b/src/css/main-snake.css new file mode 100755 index 00000000..7938bf34 --- /dev/null +++ b/src/css/main-snake.css @@ -0,0 +1,140 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ +body { + margin: 0px; + padding: 0px; + background-color: #fc5454; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} + +#game-area:focus { + outline: none; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #fcfc54; +} + +a.snake-link:hover { + color: #ffff54; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: black; + color: white; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #ffffff; + text-align: center; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("./images/snakeblock.png"), + url("./images/snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("./images/deadblock.png"), url("./images/deadblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + border: 0px solid #000080; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: #0000a8; + border: 0px solid #0000a8; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #000000; + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/matrix-snake.css b/src/css/matrix-snake.css new file mode 100644 index 00000000..643d6960 --- /dev/null +++ b/src/css/matrix-snake.css @@ -0,0 +1,134 @@ +body { +margin:0px; +padding:0px; +background-color: #00ff11; +} + +#game-area { +margin:10px; +padding:0px; +background-color: #00ff11; +} + +#mode-wrapper { +color: #000000; +font-family: Verdana, arial, helvetica, sans-serif; +font-size: 14px; + +} + +#game-area:focus { outline: none; } + +a.snake-link, a.snake-link:link, a.snake-link:visited { +color: #FCFC54; +} + +a.snake-link:hover { +color: #FfFf54; +} + +.snake-pause-screen { +font-family: Verdana, arial, helvetica, sans-serif; +font-size: 14px; +position:absolute; +width:300px; +height:80px; +text-align:center; +top:50%; +left:50%; +margin-top:-40px; +margin-left:-150px; +display:none; +background-color:black; +color:white; +} + +.snake-panel-component { +position: absolute; +font-family: Verdana, arial, helvetica, sans-serif; +font-size: 14px; +color: #000000; +text-align: center; +background-color: #00ff11; +padding: 8px; +margin: 0px; +} + +.snake-snakebody-block { +margin: 0px; +padding: 0px; +background-color: #FF0000; +position: absolute; +border: 0px solid #000080; +background-repeat: no-repeat; +} + +.snake-snakebody-alive { +background-image: url('src/css/images/matrix-snake-block.png'); +} +.snake-snakebody-dead { +background-image: url('src/css/images/deadblock.png'); +} + +.snake-food-block { +margin: 0px; +padding: 0px; +background-color: #FF0000; +border: 0px solid #000080; +position: absolute; +background-image: url("src/css/images/matrix-food-block.png") +} + +.snake-playing-field { +margin: 0px; +padding: 0px; +position: absolute; +background-color: #000000; +border: 0px solid #000000; +} + +.snake-game-container { +margin: 0px; +padding: 0px; +border-width: 0px; +border-style: none; +zoom: 1; +background-color: #00ff11; +position: relative; +} + +.snake-welcome-dialog { +padding: 8px; +margin: 0px; +background-color: #000000; +color: #00ff11; +font-family: Verdana, arial, helvetica, sans-serif; +font-size: 14px; +position: absolute; +top: 50%; +left: 50%; +width: 300px; +/height: 150px;/ +margin-top: -100px; +margin-left: -158px; +text-align: center; +display: block; +} + +.snake-try-again-dialog, .snake-win-dialog { +padding: 8px; +margin: 0px; +background-color: #000000; +color: #ff0000; +font-family: Verdana, arial, helvetica, sans-serif; +font-size: 14px; +position: absolute; +top: 50%; +left: 50%; +width: 300px; +height: 100px; +margin-top: -75px; +margin-left: -158px; +text-align: center; +display: none; +} diff --git a/src/css/neon-snake.css b/src/css/neon-snake.css new file mode 100644 index 00000000..49591510 --- /dev/null +++ b/src/css/neon-snake.css @@ -0,0 +1,145 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ +body { + margin: 0px; + padding: 0px; + background-color: #000000; +} + +.snake-toolbar { + color: #ffffff; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#mode-wrapper { + color: #ffffff; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; +} + +#game-area:focus { + outline: none; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: #00ffe0; +} + +a.snake-link:hover { + color: #0fff00; +} + +.snake-pause-screen { + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: #0fff00; + color: #000000; +} + +.snake-panel-component { + position: absolute; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + color: #ffffff; + text-align: center; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + position: absolute; + border: 0px solid #000080; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/neon-body-snakeblock.png"), + url("src/cssss/images/neon-body-snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/neon-dead-snakeblock.png"), + url("src/cssss/images/neon-dead-snakeblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: #ff0000; + border: 0px solid #000080; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: #00ffd4; + border: 0px solid #0000a8; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: #0fff00; + color: #000000; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: #0fff00; + color: #000000; + font-family: Verdana, arial, helvetica, sans-serif; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/css/teal-snake.css b/src/css/teal-snake.css new file mode 100644 index 00000000..ac06471a --- /dev/null +++ b/src/css/teal-snake.css @@ -0,0 +1,148 @@ +/* +JavaScript Snake +By Patrick Gillespie +http://patorjk.com/games/snake +*/ +@import url("https://fonts.googleapis.com/css2?family=Electrolize&display=swap"); + +body { + margin: 0px; + padding: 0px; + background-color: teal; +} + +.snake-toolbar { + font-family: Electrolize; + color: white; +} + +#game-area { + margin: 10px; + padding: 0px; +} + +#game-area:focus { + outline: none; +} + +#mode-wrapper { + font-family: Electrolize; + font-size: 14px; + color: whitesmoke; +} + +a.snake-link, +a.snake-link:link, +a.snake-link:visited { + color: white; +} + +a.snake-link:hover { + color: white; +} + +.snake-pause-screen { + font-family: Electrolize; + font-size: 16px; + position: absolute; + width: 300px; + height: 80px; + text-align: center; + top: 50%; + left: 50%; + margin-top: -40px; + margin-left: -150px; + display: none; + background-color: #3e2e44; + color: whitesmoke; +} + +.snake-panel-component { + position: absolute; + font-family: Electrolize; + font-size: 16px; + color: #938996; + text-align: center; + background-color: #3e2e44; + padding: 8px; + margin: 0px; +} + +.snake-snakebody-block { + margin: 0px; + padding: 0px; + background-color: orange; + position: absolute; + border: 0px solid black; + background-repeat: no-repeat; +} + +.snake-snakebody-alive { + background-image: url("src/css/images/snakeblock.png"); +} +.snake-snakebody-dead { + background-image: url("src/css/images/dead-dark-snakeblock.png"); +} + +.snake-food-block { + margin: 0px; + padding: 0px; + background-color: red; + border: 2px solid black; + position: absolute; +} + +.snake-playing-field { + margin: 0px; + padding: 0px; + position: absolute; + background-color: rgb(0, 180, 180); + border: 3px solid black; +} + +.snake-game-container { + margin: 0px; + padding: 0px; + border-width: 0px; + border-style: none; + zoom: 1; + background-color: #3e2e44; + position: relative; +} + +.snake-welcome-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: whitesmoke; + font-family: Electrolize; + font-size: 14px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + /*height: 150px;*/ + margin-top: -100px; + margin-left: -158px; + text-align: center; + display: block; +} + +.snake-try-again-dialog, +.snake-win-dialog { + padding: 8px; + margin: 0px; + background-color: black; + color: whitesmoke; + font-family: Electrolize; + font-size: 16px; + position: absolute; + top: 50%; + left: 50%; + width: 300px; + height: 100px; + margin-top: -75px; + margin-left: -158px; + text-align: center; + display: none; +} diff --git a/src/index.html b/src/index.html new file mode 100755 index 00000000..03dc6b10 --- /dev/null +++ b/src/index.html @@ -0,0 +1,151 @@ + + + + + + JavaScript Snake + + + + + + +
+
+ Theme: + +
+
+ Mode: + +
+ +
+
+ +
+ +
+ + + + + diff --git a/src/js/ai-init.js b/src/js/ai-init.js new file mode 100644 index 00000000..5f3e46bb --- /dev/null +++ b/src/js/ai-init.js @@ -0,0 +1,58 @@ +const mySnakeBoard = new SNAKE.Board({ + boardContainer: "game-area", + fullScreen: true, + premoveOnPause: false, + moveSnakeWithAI: ({ + grid, + snakeHead, + currentDirection, + isFirstGameMove, + setDirection, + }) => { + + /* + Direction: + 0 + 3 1 + 2 + */ + + // This is NOT a real hamiltonian cycle. It misses some values, I'm just including this here as an example of + // a look-up type table that you could do. + const hamiltonianCycleGrid = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 0], + [0, 0, 2, 3, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 0], + [0, 0, 2, 0, 0, 3, 0, 2, 0, 2, 0, 2, 0, 2, 0], + [0, 0, 2, 0, 2, 2, 0, 2, 0, 2, 0, 2, 0, 2, 0], + [0, 0, 3, 0, 3, 3, 3, 3, 0, 3, 0, 3, 0, 3, 0], + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + ] + + console.log(JSON.parse(JSON.stringify(grid))) + console.log(snakeHead, currentDirection) + + const newDirection = hamiltonianCycleGrid[snakeHead.row][snakeHead.col]; + console.log(newDirection); + setDirection(newDirection); + }, + onLengthUpdate: (length) => { + console.log(`Length: ${length}`); + }, + onPauseToggle: (isPaused) => { + console.log(`Is paused: ${isPaused}`); + }, + onInit: (params) => { + console.log("init!"); + console.log(params); + params.startAIGame(); + }, + onWin: (params) => { + console.log("win!"); + //params.startAIGame(); + }, + onDeath: (params) => { + console.log("dead!"); + //params.startAIGame(); + }, +}); diff --git a/src/js/init.js b/src/js/init.js new file mode 100644 index 00000000..daddd8b8 --- /dev/null +++ b/src/js/init.js @@ -0,0 +1,21 @@ +const mySnakeBoard = new SNAKE.Board({ + boardContainer: "game-area", + fullScreen: true, + premoveOnPause: false, + onLengthUpdate: (length) => { + console.log(`Length: ${length}`); + }, + onPauseToggle: (isPaused) => { + console.log(`Is paused: ${isPaused}`); + }, + onInit: (params) => { + console.log("init!"); + console.log(params); + }, + onWin: () => { + console.log("wn!"); + }, + onDeath: () => { + console.log("dead!"); + }, +}); diff --git a/src/js/snake.js b/src/js/snake.js new file mode 100644 index 00000000..6232e8d9 --- /dev/null +++ b/src/js/snake.js @@ -0,0 +1,1411 @@ +/* +JavaScript Snake +First version by Patrick Gillespie - I've since merged in a good number of github pull requests +http://patorjk.com/games/snake +*/ + +/** + * @module Snake + * @class SNAKE + */ + +// this will allow us to access the game in other JS files when the app is loaded up in a codesandbox.com sandbox, that's the only reason it's here +if (!window.SNAKE) { + window.SNAKE = {}; +} + +/* + Direction explained (0 = up, etc etc) + 0 + 3 1 + 2 +*/ +const MOVE_NONE = -1; +const MOVE_UP = 0; +const MOVE_LEFT = 3; +const MOVE_DOWN = 2; +const MOVE_RIGHT = 1; + +const MIN_SNAKE_SPEED = 25; +const RUSH_INCR = 5; + +const DEFAULT_SNAKE_SPEED = 80; + +const BOARD_NOT_READY = 0; +const BOARD_READY = 1; +const BOARD_IN_PLAY = 2; + +const HIGH_SCORE_KEY = "jsSnakeHighScore"; + +/** + * @method addEventListener + * @param {Object} obj The object to add an event listener to. + * @param {String} event The event to listen for. + * @param {Function} funct The function to execute when the event is triggered. + * @param {Boolean} evtCapturing True to do event capturing, false to do event bubbling. + */ +SNAKE.addEventListener = (function () { + if (window.addEventListener) { + return function (obj, event, funct, evtCapturing) { + obj.addEventListener(event, funct, evtCapturing); + }; + } else if (window.attachEvent) { + return function (obj, event, funct) { + obj.attachEvent("on" + event, funct); + }; + } +})(); + +/** + * @method removeEventListener + * @param {Object} obj The object to remove an event listener from. + * @param {String} event The event that was listened for. + * @param {Function} funct The function that was executed when the event is triggered. + * @param {Boolean} evtCapturing True if event capturing was done, false otherwise. + */ + +SNAKE.removeEventListener = (function () { + if (window.removeEventListener) { + return function (obj, event, funct, evtCapturing) { + obj.removeEventListener(event, funct, evtCapturing); + }; + } else if (window.detachEvent) { + return function (obj, event, funct) { + obj.detachEvent("on" + event, funct); + }; + } +})(); + +/** + * This class manages the snake which will reside inside of a SNAKE.Board object. + * @class Snake + * @constructor + * @namespace SNAKE + * @param {Object} config The configuration object for the class. Contains playingBoard (the SNAKE.Board that this snake resides in), startRow and startCol. + */ +SNAKE.Snake = + SNAKE.Snake || + (function () { + // ------------------------------------------------------------------------- + // Private static variables and methods + // ------------------------------------------------------------------------- + + const blockPool = []; + + const SnakeBlock = function () { + this.elm = null; + this.elmStyle = null; + this.row = -1; + this.col = -1; + this.next = null; + this.prev = null; + }; + + // this function is adapted from the example at http://greengeckodesign.com/blog/2007/07/get-highest-z-index-in-javascript.html + function getNextHighestZIndex(myObj) { + let highestIndex = 0, + currentIndex = 0, + ii; + for (ii in myObj) { + if (myObj[ii].elm.currentStyle) { + currentIndex = parseFloat(myObj[ii].elm.style["z-index"], 10); + } else if (window.getComputedStyle) { + currentIndex = parseFloat( + document.defaultView + .getComputedStyle(myObj[ii].elm, null) + .getPropertyValue("z-index"), + 10, + ); + } + if (!isNaN(currentIndex) && currentIndex > highestIndex) { + highestIndex = currentIndex; + } + } + return highestIndex + 1; + } + + // ------------------------------------------------------------------------- + // Contructor + public and private definitions + // ------------------------------------------------------------------------- + + /* + config options: + playingBoard - the SnakeBoard that this snake belongs too. + startRow - The row the snake should start on. + startCol - The column the snake should start on. + moveSnakeWithAI - function to move the snake with AI + */ + return function (config) { + if (!config || !config.playingBoard) { + return; + } + if (localStorage[HIGH_SCORE_KEY] === undefined) + localStorage.setItem(HIGH_SCORE_KEY, 0); + + // ----- private variables ----- + + const me = this; + const playingBoard = config.playingBoard; + const growthIncr = 5; + const columnShift = [0, 1, 0, -1]; + const rowShift = [-1, 0, 1, 0]; + let prevNode; + + let lastMove = 1, + preMove = MOVE_NONE, + isFirstGameMove = true, + currentDirection = MOVE_NONE, // 0: up, 1: left, 2: down, 3: right + snakeSpeed = DEFAULT_SNAKE_SPEED, + isDead = false, + isPaused = false; + + const modeDropdown = document.getElementById("selectMode"); + if (modeDropdown) { + modeDropdown.addEventListener("change", function (evt) { + evt = evt || {}; + let val = evt.target + ? parseInt(evt.target.value) + : DEFAULT_SNAKE_SPEED; + + if (isNaN(val)) { + val = DEFAULT_SNAKE_SPEED; + } else if (val < MIN_SNAKE_SPEED) { + val = DEFAULT_SNAKE_SPEED; + } + + snakeSpeed = val; + + setTimeout(function () { + document.getElementById("game-area").focus(); + }, 10); + }); + } + + // ----- public variables ----- + me.snakeBody = {}; + me.snakeBody["b0"] = new SnakeBlock(); // create snake head + me.snakeBody["b0"].row = config.startRow || 1; + me.snakeBody["b0"].col = config.startCol || 1; + me.snakeBody["b0"].elm = createSnakeElement(); + me.snakeBody["b0"].elmStyle = me.snakeBody["b0"].elm.style; + playingBoard.getBoardContainer().appendChild(me.snakeBody["b0"].elm); + me.snakeBody["b0"].elm.style.left = getLeftPosition(me.snakeBody["b0"]); + me.snakeBody["b0"].elm.style.top = getTopPosition(me.snakeBody["b0"]); + me.snakeBody["b0"].next = me.snakeBody["b0"]; + me.snakeBody["b0"].prev = me.snakeBody["b0"]; + + me.snakeLength = 1; + me.snakeHead = me.snakeBody["b0"]; + me.snakeTail = me.snakeBody["b0"]; + me.snakeHead.elm.className = me.snakeHead.elm.className.replace( + /\bsnake-snakebody-dead\b/, + "", + ); + me.snakeHead.elm.id = "snake-snakehead-alive"; + me.snakeHead.elm.className += " snake-snakebody-alive"; + + // ----- private methods ----- + + function getTopPosition(block) { + const num = block.row * playingBoard.getBlockHeight(); + return `${num}px`; + } + + function getLeftPosition(block) { + const num = block.col * playingBoard.getBlockWidth(); + return `${num}px`; + } + + function createSnakeElement() { + const tempNode = document.createElement("div"); + tempNode.className = "snake-snakebody-block"; + tempNode.style.left = "-1000px"; + tempNode.style.top = "-1000px"; + tempNode.style.width = playingBoard.getBlockWidth() + "px"; + tempNode.style.height = playingBoard.getBlockHeight() + "px"; + return tempNode; + } + + function createBlocks(num) { + let tempBlock; + const tempNode = createSnakeElement(); + + for (let ii = 1; ii < num; ii++) { + tempBlock = new SnakeBlock(); + tempBlock.elm = tempNode.cloneNode(true); + tempBlock.elmStyle = tempBlock.elm.style; + playingBoard.getBoardContainer().appendChild(tempBlock.elm); + blockPool[blockPool.length] = tempBlock; + } + + tempBlock = new SnakeBlock(); + tempBlock.elm = tempNode; + playingBoard.getBoardContainer().appendChild(tempBlock.elm); + blockPool[blockPool.length] = tempBlock; + } + + function recordScore() { + const highScore = localStorage[HIGH_SCORE_KEY]; + if (me.snakeLength > highScore) { + alert( + "Congratulations! You have beaten your previous high score, which was " + + highScore + + ".", + ); + localStorage.setItem(HIGH_SCORE_KEY, me.snakeLength); + } + } + + function handleEndCondition(handleFunc) { + recordScore(); + me.snakeHead.elm.style.zIndex = getNextHighestZIndex(me.snakeBody); + me.snakeHead.elm.className = me.snakeHead.elm.className.replace( + /\bsnake-snakebody-alive\b/, + "", + ); + me.snakeHead.elm.className += " snake-snakebody-dead"; + + isDead = true; + handleFunc(); + } + + // ----- public methods ----- + + me.setPaused = function (val) { + isPaused = val; + }; + me.getPaused = function () { + return isPaused; + }; + + /** + * This method sets the snake direction + * @param direction + */ + me.setDirection = (direction) => { + if (currentDirection !== lastMove) { + // Allow a queue of 1 premove so you can turn again before the first turn registers + preMove = direction; + } + if (Math.abs(direction - lastMove) !== 2 || isFirstGameMove) { + // Prevent snake from turning 180 degrees + currentDirection = direction; + isFirstGameMove = false; + } + }; + + /** + * This method is called when a user presses a key. It logs arrow key presses in "currentDirection", which is used when the snake needs to make its next move. + * @method handleArrowKeys + * @param {Number} keyNum A number representing the key that was pressed. + */ + /* + Handles what happens when an arrow key is pressed. + Direction explained (0 = up, etc etc) + 0 + 3 1 + 2 + */ + me.handleArrowKeys = function (keyNum) { + if (isDead || (isPaused && !config.premoveOnPause)) { + return; + } + + let directionFound = MOVE_NONE; + + switch (keyNum) { + case 37: + case 65: + directionFound = MOVE_LEFT; + break; + case 38: + case 87: + directionFound = MOVE_UP; + break; + case 39: + case 68: + directionFound = MOVE_RIGHT; + break; + case 40: + case 83: + directionFound = MOVE_DOWN; + break; + } + me.setDirection(directionFound); + }; + + /** + * This method is executed for each move of the snake. It determines where the snake will go and what will happen to it. This method needs to run quickly. + * @method go + */ + me.go = function () { + const oldHead = me.snakeHead, + newHead = me.snakeTail, + grid = playingBoard.grid; // cache grid for quicker lookup + + if (isPaused === true) { + setTimeout(function () { + me.go(); + }, snakeSpeed); + return; + } + + // code to execute if snake is being moved by AI + if (config.moveSnakeWithAI) { + config.moveSnakeWithAI({ + grid, + snakeHead: me.snakeHead, + currentDirection, + isFirstGameMove, + setDirection: me.setDirection, + }); + } + + me.snakeTail = newHead.prev; + me.snakeHead = newHead; + + // clear the old board position + if (grid[newHead.row] && grid[newHead.row][newHead.col]) { + grid[newHead.row][newHead.col] = 0; + } + + if (currentDirection !== MOVE_NONE) { + lastMove = currentDirection; + if (preMove !== MOVE_NONE) { + // If the user queued up another move after the current one + currentDirection = preMove; // Execute that move next time (unless overwritten) + preMove = MOVE_NONE; + } + } + + newHead.col = oldHead.col + columnShift[lastMove]; + newHead.row = oldHead.row + rowShift[lastMove]; + + if (!newHead.elmStyle) { + newHead.elmStyle = newHead.elm.style; + } + + newHead.elmStyle.left = getLeftPosition(newHead); + newHead.elmStyle.top = getTopPosition(newHead); + if (me.snakeLength > 1) { + newHead.elm.id = "snake-snakehead-alive"; + oldHead.elm.id = ""; + } + + // check the new spot the snake moved into + + if (grid[newHead.row][newHead.col] === 0) { + grid[newHead.row][newHead.col] = 1; + setTimeout(function () { + me.go(); + }, snakeSpeed); + } else if (grid[newHead.row][newHead.col] > 0) { + me.handleDeath(); + } else if ( + grid[newHead.row][newHead.col] === playingBoard.getGridFoodValue() + ) { + grid[newHead.row][newHead.col] = 1; + if (!me.eatFood()) { + me.handleWin(); + return; + } + setTimeout(function () { + me.go(); + }, snakeSpeed); + } + }; + + /** + * This method is called when it is determined that the snake has eaten some food. + * @method eatFood + * @return {bool} Whether a new food was able to spawn (true) + * or not (false) after the snake eats food. + */ + me.eatFood = function () { + if (blockPool.length <= growthIncr) { + createBlocks(growthIncr * 2); + } + const blocks = blockPool.splice(0, growthIncr); + + let ii = blocks.length, + index; + prevNode = me.snakeTail; + while (ii--) { + index = "b" + me.snakeLength++; + me.snakeBody[index] = blocks[ii]; + me.snakeBody[index].prev = prevNode; + me.snakeBody[index].elm.className = + me.snakeHead.elm.className.replace(/\bsnake-snakebody-dead\b/, ""); + me.snakeBody[index].elm.className += " snake-snakebody-alive"; + prevNode.next = me.snakeBody[index]; + prevNode = me.snakeBody[index]; + } + me.snakeTail = me.snakeBody[index]; + me.snakeTail.next = me.snakeHead; + me.snakeHead.prev = me.snakeTail; + + if (!playingBoard.foodEaten()) { + return false; + } + + //Checks if the current selected option is that of "Rush" + //If so, "increase" the snake speed + const selectDropDown = document.getElementById("selectMode"); + const selectedOption = + selectDropDown.options[selectDropDown.selectedIndex]; + + if (selectedOption.text.localeCompare("Rush") == 0) { + if (snakeSpeed > MIN_SNAKE_SPEED + RUSH_INCR) { + snakeSpeed -= RUSH_INCR; + } + } + + return true; + }; + + /** + * This method handles what happens when the snake dies. + * @method handleDeath + */ + me.handleDeath = function () { + //Reset speed + const selectedSpeed = document.getElementById("selectMode").value; + snakeSpeed = parseInt(selectedSpeed); + + handleEndCondition(playingBoard.handleDeath); + }; + + /** + * This method handles what happens when the snake wins. + * @method handleDeath + */ + me.handleWin = function () { + handleEndCondition(playingBoard.handleWin); + }; + + /** + * This method sets a flag that lets the snake be alive again. + * @method rebirth + */ + me.rebirth = function () { + isDead = false; + isFirstGameMove = true; + preMove = MOVE_NONE; + }; + + /** + * This method reset the snake so it is ready for a new game. + * @method reset + */ + me.reset = function () { + if (isDead === false) { + return; + } + + const blocks = []; + let curNode = me.snakeHead.next; + let nextNode; + + while (curNode !== me.snakeHead) { + nextNode = curNode.next; + curNode.prev = null; + curNode.next = null; + blocks.push(curNode); + curNode = nextNode; + } + me.snakeHead.next = me.snakeHead; + me.snakeHead.prev = me.snakeHead; + me.snakeTail = me.snakeHead; + me.snakeLength = 1; + + for (let ii = 0; ii < blocks.length; ii++) { + blocks[ii].elm.style.left = "-1000px"; + blocks[ii].elm.style.top = "-1000px"; + blocks[ii].elm.className = me.snakeHead.elm.className.replace( + /\bsnake-snakebody-dead\b/, + "", + ); + blocks[ii].elm.className += " snake-snakebody-alive"; + } + + blockPool.concat(blocks); + me.snakeHead.elm.className = me.snakeHead.elm.className.replace( + /\bsnake-snakebody-dead\b/, + "", + ); + me.snakeHead.elm.className += " snake-snakebody-alive"; + me.snakeHead.elm.id = "snake-snakehead-alive"; + me.snakeHead.row = config.startRow || 1; + me.snakeHead.col = config.startCol || 1; + me.snakeHead.elm.style.left = getLeftPosition(me.snakeHead); + me.snakeHead.elm.style.top = getTopPosition(me.snakeHead); + }; + + me.getSpeed = () => { + return snakeSpeed; + }; + me.setSpeed = (speed) => { + snakeSpeed = speed; + }; + + // --------------------------------------------------------------------- + // Initialize + // --------------------------------------------------------------------- + createBlocks(growthIncr * 2); + }; + })(); + +/** + * This class manages the food which the snake will eat. + * @class Food + * @constructor + * @namespace SNAKE + * @param {Object} config The configuration object for the class. Contains playingBoard (the SNAKE.Board that this food resides in). + */ + +SNAKE.Food = + SNAKE.Food || + (function () { + // ------------------------------------------------------------------------- + // Private static variables and methods + // ------------------------------------------------------------------------- + + let instanceNumber = 0; + + function getRandomPosition(x, y) { + return Math.floor(Math.random() * (y + 1 - x)) + x; + } + + // ------------------------------------------------------------------------- + // Contructor + public and private definitions + // ------------------------------------------------------------------------- + + /* + config options: + playingBoard - the SnakeBoard that this object belongs too. + */ + return function (config) { + if (!config || !config.playingBoard) { + return; + } + + // ----- private variables ----- + + const me = this; + const playingBoard = config.playingBoard; + let fRow, fColumn; + const myId = instanceNumber++; + + const elmFood = document.createElement("div"); + elmFood.setAttribute("id", "snake-food-" + myId); + elmFood.className = "snake-food-block"; + elmFood.style.width = playingBoard.getBlockWidth() + "px"; + elmFood.style.height = playingBoard.getBlockHeight() + "px"; + elmFood.style.left = "-1000px"; + elmFood.style.top = "-1000px"; + playingBoard.getBoardContainer().appendChild(elmFood); + + // ----- public methods ----- + + /** + * @method getFoodElement + * @return {DOM Element} The div the represents the food. + */ + me.getFoodElement = function () { + return elmFood; + }; + + /** + * Randomly places the food onto an available location on the playing board. + * @method randomlyPlaceFood + * @return {bool} Whether a food was able to spawn (true) or not (false). + */ + me.randomlyPlaceFood = function () { + // if there exist some food, clear its presence from the board + if ( + playingBoard.grid[fRow] && + playingBoard.grid[fRow][fColumn] === playingBoard.getGridFoodValue() + ) { + playingBoard.grid[fRow][fColumn] = 0; + } + + let row = 0, + col = 0, + numTries = 0; + + const maxRows = playingBoard.grid.length - 1; + const maxCols = playingBoard.grid[0].length - 1; + + while (playingBoard.grid[row][col] !== 0) { + row = getRandomPosition(1, maxRows); + col = getRandomPosition(1, maxCols); + + // in some cases there may not be any room to put food anywhere + // instead of freezing, exit out (and return false to indicate + // that the player beat the game) + numTries++; + if (numTries > 20000) { + return false; + } + } + + playingBoard.grid[row][col] = playingBoard.getGridFoodValue(); + fRow = row; + fColumn = col; + elmFood.style.top = row * playingBoard.getBlockHeight() + "px"; + elmFood.style.left = col * playingBoard.getBlockWidth() + "px"; + return true; + }; + }; + })(); + +/** + * This class manages playing board for the game. + * @class Board + * @constructor + * @namespace SNAKE + * @param {Object} config The configuration object for the class. Set fullScreen equal to true if you want the game to take up the full screen, otherwise, set the top, left, width and height parameters. + */ + +SNAKE.Board = + SNAKE.Board || + (function () { + // ------------------------------------------------------------------------- + // Private static variables and methods + // ------------------------------------------------------------------------- + + let instanceNumber = 0; + + // this function is adapted from the example at http://greengeckodesign.com/blog/2007/07/get-highest-z-index-in-javascript.html + function getNextHighestZIndex(myObj) { + let highestIndex = 0, + currentIndex = 0, + ii; + for (ii in myObj) { + if (myObj[ii].elm.currentStyle) { + currentIndex = parseFloat(myObj[ii].elm.style["z-index"], 10); + } else if (window.getComputedStyle) { + currentIndex = parseFloat( + document.defaultView + .getComputedStyle(myObj[ii].elm, null) + .getPropertyValue("z-index"), + 10, + ); + } + if (!isNaN(currentIndex) && currentIndex > highestIndex) { + highestIndex = currentIndex; + } + } + return highestIndex + 1; + } + + /* + This function returns the width of the available screen real estate that we have + */ + function getClientWidth() { + let myWidth = 0; + if (typeof window.innerWidth === "number") { + myWidth = window.innerWidth; //Non-IE + } else if ( + document.documentElement && + (document.documentElement.clientWidth || + document.documentElement.clientHeight) + ) { + myWidth = document.documentElement.clientWidth; //IE 6+ in 'standards compliant mode' + } else if ( + document.body && + (document.body.clientWidth || document.body.clientHeight) + ) { + myWidth = document.body.clientWidth; //IE 4 compatible + } + return myWidth; + } + + /* + This function returns the height of the available screen real estate that we have + */ + function getClientHeight() { + let myHeight = 0; + if (typeof window.innerHeight === "number") { + myHeight = window.innerHeight; //Non-IE + } else if ( + document.documentElement && + (document.documentElement.clientWidth || + document.documentElement.clientHeight) + ) { + myHeight = document.documentElement.clientHeight; //IE 6+ in 'standards compliant mode' + } else if ( + document.body && + (document.body.clientWidth || document.body.clientHeight) + ) { + myHeight = document.body.clientHeight; //IE 4 compatible + } + return myHeight; + } + + // ------------------------------------------------------------------------- + // Contructor + public and private definitions + // ------------------------------------------------------------------------- + + return function (inputConfig) { + // --- private variables --- + const me = this; + const myId = instanceNumber++; + const config = inputConfig || {}; + const MAX_BOARD_COLS = 250; + const MAX_BOARD_ROWS = 250; + const blockWidth = 20; + const blockHeight = 20; + const GRID_FOOD_VALUE = -1; // the value of a spot on the board that represents snake food; MUST BE NEGATIVE + + // defaults + if (!config.onLengthUpdate) { + config.onLengthUpdate = () => {}; + } + + if (!config.onPauseToggle) { + config.onPauseToggle = () => {}; + } + if (!config.onWin) { + config.onWin = () => {}; + } + if (!config.onDeath) { + config.onDeath = () => {}; + } + + let myFood, + mySnake, + boardState = BOARD_READY, // 0: in active, 1: awaiting game start, 2: playing game + myKeyListener, + myWindowListener, + isPaused = false; //note: both the board and the snake can be paused + + // Board components + let elmContainer, + elmPlayingField, + elmAboutPanel, + elmLengthPanel, + elmHighscorePanel, + elmWelcome, + elmTryAgain, + elmWin, + elmPauseScreen; + + // --- public variables --- + me.grid = []; + + // --------------------------------------------------------------------- + // private functions + // --------------------------------------------------------------------- + + function getStartRow() { + return config.startRow || 2; + } + + function getStartCol() { + return config.startCol || 2; + } + + function createBoardElements() { + elmPlayingField = document.createElement("div"); + elmPlayingField.setAttribute("id", "playingField"); + elmPlayingField.className = "snake-playing-field"; + + SNAKE.addEventListener( + elmPlayingField, + "click", + function () { + elmContainer.focus(); + }, + false, + ); + + elmPauseScreen = document.createElement("div"); + elmPauseScreen.className = "snake-pause-screen"; + elmPauseScreen.innerHTML = + "
[Paused]

Press [space] to unpause.

"; + + elmAboutPanel = document.createElement("div"); + elmAboutPanel.className = "snake-panel-component"; + elmAboutPanel.innerHTML = + "more patorjk.com apps - source code - pat's youtube"; + + elmLengthPanel = document.createElement("div"); + elmLengthPanel.className = "snake-panel-component"; + elmLengthPanel.innerHTML = "Length: 1"; + + elmHighscorePanel = document.createElement("div"); + elmHighscorePanel.className = "snake-panel-component"; + elmHighscorePanel.innerHTML = + "Highscore: " + (localStorage[HIGH_SCORE_KEY] || 0); + + // if it's not AI, show the dialogs + if (!config.moveSnakeWithAI) { + elmWelcome = createWelcomeElement(); + elmTryAgain = createTryAgainElement(); + elmWin = createWinElement(); + } + + SNAKE.addEventListener( + elmContainer, + "keyup", + function (evt) { + if (!evt) evt = window.event; + evt.cancelBubble = true; + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + return false; + }, + false, + ); + + elmContainer.className = "snake-game-container"; + + elmPauseScreen.style.zIndex = 10000; + elmContainer.appendChild(elmPauseScreen); + elmContainer.appendChild(elmPlayingField); + elmContainer.appendChild(elmAboutPanel); + elmContainer.appendChild(elmLengthPanel); + elmContainer.appendChild(elmHighscorePanel); + + // nothing to attach if using AI + if (!config.moveSnakeWithAI) { + elmContainer.appendChild(elmWelcome); + elmContainer.appendChild(elmTryAgain); + elmContainer.appendChild(elmWin); + } + + mySnake = new SNAKE.Snake({ + playingBoard: me, + startRow: getStartRow(), + startCol: getStartCol(), + premoveOnPause: config.premoveOnPause, + moveSnakeWithAI: config.moveSnakeWithAI, + }); + myFood = new SNAKE.Food({ playingBoard: me }); + + if (elmWelcome) { + elmWelcome.style.zIndex = 1000; + } + } + + function maxBoardWidth() { + return MAX_BOARD_COLS * me.getBlockWidth(); + } + + function maxBoardHeight() { + return MAX_BOARD_ROWS * me.getBlockHeight(); + } + + function createWelcomeElement() { + const tmpElm = document.createElement("div"); + tmpElm.id = "sbWelcome" + myId; + tmpElm.className = "snake-welcome-dialog"; + + const welcomeTxt = document.createElement("div"); + let fullScreenText = ""; + if (config.fullScreen) { + fullScreenText = "On Windows, press F11 to play in Full Screen mode."; + } + welcomeTxt.innerHTML = + "JavaScript Snake

Use the arrow keys on your keyboard to play the game. " + + fullScreenText + + "

"; + const welcomeStart = document.createElement("button"); + welcomeStart.appendChild(document.createTextNode("Play Game")); + + const loadGame = function () { + SNAKE.removeEventListener(window, "keyup", kbShortcut, false); + tmpElm.style.display = "none"; + me.setBoardState(BOARD_READY); + me.getBoardContainer().focus(); + }; + + const kbShortcut = function (evt) { + if (!evt) evt = window.event; + const keyNum = evt.which ? evt.which : evt.keyCode; + if (keyNum === 32 || keyNum === 13) { + loadGame(); + } + }; + + SNAKE.addEventListener(window, "keyup", kbShortcut, false); + SNAKE.addEventListener(welcomeStart, "click", loadGame, false); + + tmpElm.appendChild(welcomeTxt); + tmpElm.appendChild(welcomeStart); + return tmpElm; + } + + function createGameEndElement(message, elmId, elmClassName) { + const tmpElm = document.createElement("div"); + tmpElm.id = elmId + myId; + tmpElm.className = elmClassName; + + const gameEndTxt = document.createElement("div"); + gameEndTxt.innerHTML = "JavaScript Snake

" + message + "

"; + const gameEndStart = document.createElement("button"); + gameEndStart.appendChild(document.createTextNode("Play Again?")); + + const reloadGame = function () { + tmpElm.style.display = "none"; + me.resetBoard(); + me.setBoardState(BOARD_READY); + me.getBoardContainer().focus(); + }; + + const kbGameEndShortcut = function (evt) { + if (boardState !== 0 || tmpElm.style.display !== "block") { + return; + } + if (!evt) evt = window.event; + const keyNum = evt.which ? evt.which : evt.keyCode; + if (keyNum === 32 || keyNum === 13) { + reloadGame(); + } + }; + SNAKE.addEventListener(window, "keyup", kbGameEndShortcut, true); + + SNAKE.addEventListener(gameEndStart, "click", reloadGame, false); + tmpElm.appendChild(gameEndTxt); + tmpElm.appendChild(gameEndStart); + return tmpElm; + } + + function createTryAgainElement() { + return createGameEndElement( + "You died :(", + "sbTryAgain", + "snake-try-again-dialog", + ); + } + + function createWinElement() { + return createGameEndElement("You win! :D", "sbWin", "snake-win-dialog"); + } + + function handleEndCondition(elmDialog) { + const index = Math.max( + getNextHighestZIndex(mySnake.snakeBody), + getNextHighestZIndex({ tmp: { elm: myFood.getFoodElement() } }), + ); + if (elmDialog) { + elmContainer.removeChild(elmDialog); + elmContainer.appendChild(elmDialog); + elmDialog.style.zIndex = index; + elmDialog.style.display = "block"; + } + me.setBoardState(BOARD_NOT_READY); + } + + // --------------------------------------------------------------------- + // public functions + // --------------------------------------------------------------------- + + me.setPaused = function (val) { + isPaused = val; + mySnake.setPaused(val); + if (isPaused) { + elmPauseScreen.style.display = "block"; + } else { + elmPauseScreen.style.display = "none"; + } + config.onPauseToggle(isPaused); + }; + me.getPaused = function () { + return isPaused; + }; + + /** + * Resets the playing board for a new game. + * @method resetBoard + */ + me.resetBoard = function () { + SNAKE.removeEventListener( + elmContainer, + "keydown", + myKeyListener, + false, + ); + SNAKE.removeEventListener( + elmContainer, + "visibilitychange", + myWindowListener, + false, + ); + mySnake.reset(); + config.onLengthUpdate(1); + elmLengthPanel.innerHTML = "Length: 1"; + me.setupPlayingField(); + me.grid[getStartRow()][getStartCol()] = 1; // snake head + }; + /** + * Gets the current state of the playing board. There are 3 states: 0 - Welcome or Try Again dialog is present. 1 - User has pressed "Start Game" on the Welcome or Try Again dialog but has not pressed an arrow key to move the snake. 2 - The game is in progress and the snake is moving. + * @method getBoardState + * @return {Number} The state of the board. + */ + me.getBoardState = function () { + return boardState; + }; + /** + * Sets the current state of the playing board. There are 3 states: 0 - Welcome or Try Again dialog is present. 1 - User has pressed "Start Game" on the Welcome or Try Again dialog but has not pressed an arrow key to move the snake. 2 - The game is in progress and the snake is moving. + * @method setBoardState + * @param {Number} state The state of the board. + */ + me.setBoardState = function (state) { + boardState = state; + }; + /** + * @method getGridFoodValue + * @return {Number} A number that represents food on a number representation of the playing board. + */ + me.getGridFoodValue = function () { + return GRID_FOOD_VALUE; + }; + /** + * @method getPlayingFieldElement + * @return {DOM Element} The div representing the playing field (this is where the snake can move). + */ + me.getPlayingFieldElement = function () { + return elmPlayingField; + }; + /** + * @method setBoardContainer + * @param {DOM Element or String} myContainer Sets the container element for the game. + */ + me.setBoardContainer = function (myContainer) { + if (typeof myContainer === "string") { + myContainer = document.getElementById(myContainer); + } + if (myContainer === elmContainer) { + return; + } + elmContainer = myContainer; + elmPlayingField = null; + me.setupPlayingField(); + me.grid[getStartRow()][getStartCol()] = 1; // snake head + }; + /** + * @method getBoardContainer + * @return {DOM Element} + */ + me.getBoardContainer = function () { + return elmContainer; + }; + /** + * @method getBlockWidth + * @return {Number} + */ + me.getBlockWidth = function () { + return blockWidth; + }; + /** + * @method getBlockHeight + * @return {Number} + */ + me.getBlockHeight = function () { + return blockHeight; + }; + /** + * Sets up the playing field. + * @method setupPlayingField + */ + me.setupPlayingField = function () { + if (!elmPlayingField) { + createBoardElements(); + } // create playing field + + // calculate width of our game container + let cWidth, cHeight; + let cTop, cLeft; + if (config.fullScreen === true) { + cTop = 0; + cLeft = 0; + cWidth = getClientWidth() - 20; + cHeight = getClientHeight() - 20; + } else { + cTop = config.top; + cLeft = config.left; + cWidth = config.width; + cHeight = config.height; + } + + // define the dimensions of the board and playing field + const wEdgeSpace = + me.getBlockWidth() * 2 + (cWidth % me.getBlockWidth()); + const fWidth = Math.min( + maxBoardWidth() - wEdgeSpace, + cWidth - wEdgeSpace, + ); + const hEdgeSpace = + me.getBlockHeight() * 3 + (cHeight % me.getBlockHeight()); + const fHeight = Math.min( + maxBoardHeight() - hEdgeSpace, + cHeight - hEdgeSpace, + ); + + elmContainer.style.left = cLeft + "px"; + elmContainer.style.top = cTop + "px"; + elmContainer.style.width = cWidth + "px"; + elmContainer.style.height = cHeight + "px"; + elmPlayingField.style.left = me.getBlockWidth() + "px"; + elmPlayingField.style.top = me.getBlockHeight() + "px"; + elmPlayingField.style.width = fWidth + "px"; + elmPlayingField.style.height = fHeight + "px"; + + // the math for this will need to change depending on font size, padding, etc + // assuming height of 14 (font size) + 8 (padding) + const bottomPanelHeight = hEdgeSpace - me.getBlockHeight(); + const pLabelTop = + me.getBlockHeight() + + fHeight + + Math.round((bottomPanelHeight - 30) / 2) + + "px"; + + elmAboutPanel.style.top = pLabelTop; + elmAboutPanel.style.width = "450px"; + elmAboutPanel.style.left = + Math.round(cWidth / 2) - Math.round(450 / 2) + "px"; + + elmLengthPanel.style.top = pLabelTop; + elmLengthPanel.style.left = 30 + "px"; + + elmHighscorePanel.style.top = pLabelTop; + elmHighscorePanel.style.left = cWidth - 140 + "px"; + + // if width is too narrow, hide the about panel + if (cWidth < 700) { + elmAboutPanel.style.display = "none"; + } else { + elmAboutPanel.style.display = "block"; + } + + me.grid = []; + const numBoardCols = fWidth / me.getBlockWidth() + 2; + const numBoardRows = fHeight / me.getBlockHeight() + 2; + + for (let row = 0; row < numBoardRows; row++) { + me.grid[row] = []; + for (let col = 0; col < numBoardCols; col++) { + if ( + col === 0 || + row === 0 || + col === numBoardCols - 1 || + row === numBoardRows - 1 + ) { + me.grid[row][col] = 1; // an edge + } else { + me.grid[row][col] = 0; // empty space + } + } + } + + myFood.randomlyPlaceFood(); + config.onLengthUpdate(1); + + myKeyListener = function (evt) { + if (!evt) evt = window.event; + const keyNum = evt.which ? evt.which : evt.keyCode; + + if (me.getBoardState() === BOARD_READY) { + if ( + !(keyNum >= 37 && keyNum <= 40) && + !( + keyNum === 87 || + keyNum === 65 || + keyNum === 83 || + keyNum === 68 + ) + ) { + return; + } // if not an arrow key, leave + + // This removes the listener added at the #listenerX line + SNAKE.removeEventListener( + elmContainer, + "keydown", + myKeyListener, + false, + ); + SNAKE.removeEventListener( + elmContainer, + "visibilitychange", + myWindowListener, + false, + ); + + myKeyListener = function (evt) { + if (!evt) evt = window.event; + const keyNum = evt.which ? evt.which : evt.keyCode; + + if (keyNum === 32) { + if (me.getBoardState() != BOARD_NOT_READY) + me.setPaused(!me.getPaused()); + } + + mySnake.handleArrowKeys(keyNum); + + evt.cancelBubble = true; + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + return false; + }; + + //listener for pausing the game if user change tab or minimize the browser window + document.addEventListener("visibilitychange", () => { + if (document.visibilityState === "hidden") { + if (me.getBoardState() != BOARD_NOT_READY && !me.getPaused()) + me.setPaused(true); + } + }); + + SNAKE.addEventListener( + elmContainer, + "keydown", + myKeyListener, + false, + ); + SNAKE.addEventListener( + elmContainer, + "visibilitychange", + myWindowListener, + false, + ); + + mySnake.rebirth(); + mySnake.handleArrowKeys(keyNum); + me.setBoardState(BOARD_IN_PLAY); // start the game! + mySnake.go(); + } + + evt.cancelBubble = true; + if (evt.stopPropagation) { + evt.stopPropagation(); + } + if (evt.preventDefault) { + evt.preventDefault(); + } + return false; + }; + + // Search for #listenerX to see where this is removed + if (!config.moveSnakeWithAI) { + SNAKE.addEventListener(elmContainer, "keydown", myKeyListener, false); + SNAKE.addEventListener( + elmContainer, + "visibilitychange", + myWindowListener, + false, + ); + } + }; + + /** + * This method is called when the snake has eaten some food. + * @method foodEaten + * @return {bool} Whether a new food was able to spawn (true) + * or not (false) after the snake eats food. + */ + me.foodEaten = function () { + config.onLengthUpdate(mySnake.snakeLength); + elmLengthPanel.innerHTML = "Length: " + mySnake.snakeLength; + if (mySnake.snakeLength > localStorage[HIGH_SCORE_KEY]) { + localStorage.setItem(HIGH_SCORE_KEY, mySnake.snakeLength); + elmHighscorePanel.innerHTML = + "Highscore: " + localStorage[HIGH_SCORE_KEY]; + } + if (!myFood.randomlyPlaceFood()) { + return false; + } + return true; + }; + + /** + * This method is called when the snake dies. + * @method handleDeath + */ + me.handleDeath = function () { + handleEndCondition(elmTryAgain); + config.onDeath({ startAIGame: me.startAIGame }); + }; + + /** + * This method is called when the snake wins. + * @method handleWin + */ + me.handleWin = function () { + handleEndCondition(elmWin); + config.onWin({ startAIGame: me.startAIGame }); + }; + + me.setSpeed = (speed) => { + mySnake.setSpeed(speed); + }; + me.getSpeed = () => { + return mySnake.getSpeed(); + }; + + me.startAIGame = () => { + me.resetBoard(); + mySnake.rebirth(); + me.setBoardState(BOARD_IN_PLAY); // start the game! + mySnake.go(); + }; + + // --------------------------------------------------------------------- + // Initialize + // --------------------------------------------------------------------- + + config.fullScreen = + typeof config.fullScreen === "undefined" ? false : config.fullScreen; + config.top = typeof config.top === "undefined" ? 0 : config.top; + config.left = typeof config.left === "undefined" ? 0 : config.left; + config.width = typeof config.width === "undefined" ? 400 : config.width; + config.height = + typeof config.height === "undefined" ? 400 : config.height; + config.premoveOnPause = + typeof config.premoveOnPause === "undefined" + ? false + : config.premoveOnPause; + + if (config.fullScreen) { + SNAKE.addEventListener( + window, + "resize", + function () { + me.setupPlayingField(); + }, + false, + ); + } + + me.setBoardState(BOARD_NOT_READY); + + if (config.boardContainer) { + me.setBoardContainer(config.boardContainer); + } + + const reloadGame = function () { + me.resetBoard(); + me.setBoardState(BOARD_READY); + me.getBoardContainer().focus(); + }; + + if (config.onInit) { + config.onInit({ + reloadGame, + getSpeed: me.getSpeed, + setSpeed: me.setSpeed, + startAIGame: me.startAIGame, + }); + } + }; // end return function + })();