diff --git a/.gitignore b/.gitignore index c5205ee4..57e06c65 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ web-ext-artifacts/ node_modules/ +dist/ .idea diff --git a/background.js b/background.js deleted file mode 100644 index 6572766d..00000000 --- a/background.js +++ /dev/null @@ -1,27 +0,0 @@ -chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { - let url; - const options = {}; - - if (request.model) { - url = `https://money.csgofloat.com/model?url=${request.inspectLink}`; - } else if (request.price) { - url = `https://money.csgofloat.com/price?name=${request.name}`; - } else if (request.inventory) { - url = `https://steamcommunity.com/profiles/${request.steamId}/inventory/json/730/2?l=english`; - } else if (request.floatMarket) { - options.credentials = 'include'; - url = `https://csgofloat.com/api/v1/me/pending-trades`; - } else if (request.stall) { - url = `https://csgofloat.com/api/v1/users/${request.steamId}/stall`; - } else { - url = `https://api.csgofloat.com/?url=${request.inspectLink}&minimal=true${request.listPrice ? '&listPrice=' + request.listPrice : ''}`; - } - - fetch(url, options) - .then(response => { - response.json().then(data => sendResponse(data)); - }) - .catch(err => sendResponse(err)); - - return true; -}); diff --git a/float.css b/float.css deleted file mode 100644 index 741596ff..00000000 --- a/float.css +++ /dev/null @@ -1,187 +0,0 @@ -#floatFilter hr { - background-color: #1b2939; - border-style: solid none none; - border-color: black; - border-width: 1px 0 0; - height: 2px; -} - -#floatUtilities { - padding: 10px; - margin-top: 10px; - background-color: rgba(0, 0, 0, 0.2); -} - -#floatBanner { - padding: 4px; - margin-top: 10px; - background-color: rgba(0, 0, 0, 0.2); - text-align: center; -} - -.float-money-button { - padding: 10px 10px 10px 10px; - background-color: transparent; - border: 2px white solid; - color: white; - font-family: 'Motiva Sans', Sans-serif, serif; - font-size: 16px; -} - -.float-money-button img { - vertical-align: middle; -} - -.easy-inspect { - position: absolute; - height: 74px; - width: 74px; - left: 0; - top: 0; - vertical-align: middle; - text-align: center; - line-height: 74px; - display: none; - font-size: 20px; -} - -.market_recent_listing_row:hover .easy-inspect { - display: initial; -} - -#pageSize { - margin-left: 10px; -} - -#compileStatus { - display: inline; - text-align: left; - margin-left: 5px; -} - -#compileStatus[error='false'] { - color: green; -} - -#compileStatus[error='true'] { - color: red; -} - -#compileError { - font-family: Consolas; - margin-top: 5px; -} - -input[type='color'] { - -webkit-appearance: none; - border: none; - width: 20px; - height: 20px; - padding: 0; -} - -input[type='color']::-webkit-color-swatch-wrapper { - padding: 0; -} - -input[type='color']::-webkit-color-swatch { - border: none; -} - -.float-github { - margin-left: 10px; - text-decoration: underline; - font-family: 'Motiva Sans', sans-serif; -} - -.float-stickers-container { - float: right; -} - -.float-div { - display: block; - text-align: left; - margin-bottom: 5px; -} - -.float-model-frame { - width: 100%; - height: 500px; - z-index: 999; - border-width: 0; - margin-top: 10px; -} - -.float-screenshot-frame { - width: 100%; -} - -.float-tooltip { - position: relative; - display: inline-block; -} - -.float-tooltip .tiptext { - visibility: hidden; - width: 200px; - color: #ffffff; - text-align: center; - - padding: 5px 0; - border-radius: 4px; - bottom: 100%; - left: 50%; - margin-left: -100px; - margin-bottom: 5px; - - position: absolute; - z-index: 1; -} - -.float-tooltip:hover .tiptext { - visibility: visible; -} - -.float-shine { - overflow: hidden; -} - -/* Based on https://jsfiddle.net/AntonTrollback/nqQc7/ */ -.float-shine:after { - animation: shine 4s ease-in-out infinite; - content: ""; - position: absolute; - top: -110%; - left: -210%; - width: 200%; - height: 200%; - opacity: 0; - transform: rotate(30deg); - - background: rgba(255, 255, 255, 0.13); - background: linear-gradient( - to right, - rgba(255, 255, 255, 0) 0%, - rgba(255, 255, 255, 0.13) 77%, - rgba(255, 255, 255, 0.5) 92%, - rgba(255, 255, 255, 0) 100% - ); -} - -@keyframes shine{ - 50% { - opacity: 1; - top: -30%; - left: -30%; - transition-property: left, top, opacity; - transition-duration: 0.7s, 0.7s, 0.15s; - transition-timing-function: ease; - } - 100% { - opacity: 0; - top: -30%; - left: -30%; - transition-property: left, top, opacity; - } -} - diff --git a/float.js b/float.js deleted file mode 100644 index 48b4c3a9..00000000 --- a/float.js +++ /dev/null @@ -1,843 +0,0 @@ -let floatData = {}, inventory = {}; -let walletInfo = {}; -let sortTypeAsc = true; -let floatUtilitiesAdded = false; -const filters = new Filters(); -const queue = new Queue(); - -const version = chrome.runtime.getManifest().version; - -const getRankColour = function (rank) { - switch (rank) { - case 1: - return '#c3a508'; - case 2: - case 3: - return '#9a9999'; - case 4: - case 5: - return '#8a5929'; - default: - return ''; - } -}; - -const showFloat = async function(listingId) { - let itemInfo = floatData[listingId]; - - let floatDivs = document.querySelectorAll(`#item_${listingId}_floatdiv`); - - for (const floatDiv of floatDivs) { - // Remove the "get float" button - let floatButton = floatDiv.querySelector('#getFloatBtn'); - if (floatButton) floatDiv.removeChild(floatButton); - - // Remove message div - let msgdiv = floatDiv.querySelector('.floatmessage'); - if (msgdiv) floatDiv.removeChild(msgdiv); - - // Add the float value - let itemFloatDiv = floatDiv.querySelector('.csgofloat-itemfloat'); - - if (itemFloatDiv) { - itemFloatDiv.innerHTML = ''; - - const floatText = floatDiv.minimal - ? itemInfo.floatvalue.toFixed(6) - : `Float: ${itemInfo.floatvalue.toFixed(14)}`; - - itemFloatDiv.appendChild(document.createTextNode(floatText)); - - // Get whichever is the lower rank - const rank = (itemInfo.low_rank || 1001) < (itemInfo.high_rank || 1001) ? - itemInfo.low_rank : itemInfo.high_rank; - - if (rank && rank <= 1000) { - if (floatDiv.minimal) { - itemFloatDiv.appendChild(document.createTextNode(` (#${rank})`)); - } else { - itemFloatDiv.appendChild(getRankLink(itemInfo, rank)); - } - } - - if (rank <= 5 && floatDiv.minimal) { - // Make the inventory box coloured ;) - floatDiv.parentNode.style.color = 'black'; - floatDiv.parentNode.querySelector('img').style.backgroundColor = getRankColour(rank); - floatDiv.parentNode.classList.add('float-shine'); - } - } - - // Add the paint seed - let seedDiv = floatDiv.querySelector('.csgofloat-itemseed'); - if (seedDiv) { - let seedText = floatDiv.minimal ? itemInfo.paintseed : `Paint Seed: ${itemInfo.paintseed}`; - if (hasDopplerPhase(itemInfo.paintindex)) { - seedText += ` (${getDopplerPhase(itemInfo.paintindex)})`; - } - seedDiv.innerText = seedText; - if (!floatDiv.minimal) { - seedDiv.style.marginBottom = '10px'; - } - } - - // Set the wear value for each sticker - for (let stickerIndex = 0; stickerIndex < itemInfo.stickers.length; stickerIndex++) { - const sticker = itemInfo.stickers[stickerIndex]; - - // Check if the sticker div exists - const stickerWearDiv = floatDiv.parentNode.querySelector(`#sticker_${stickerIndex}_wear`); - if (stickerWearDiv) { - stickerWearDiv.innerText = Math.round(100 * (sticker.wear || 0)) + '%'; - } - } - - const wearRange = rangeFromWear(itemInfo.floatvalue) || [0, 1]; - - const vars = { - float: itemInfo.floatvalue, - seed: itemInfo.paintseed, - minfloat: itemInfo.min, - maxfloat: itemInfo.max, - minwearfloat: wearRange[0], - maxwearfloat: wearRange[1], - phase: (getDopplerPhase(itemInfo.paintindex) || '').replace('Phase', '').trim(), - low_rank: parseInt(itemInfo.low_rank), - high_rank: parseInt(itemInfo.high_rank) - }; - - const listingInfo = steamListingInfo[listingId]; - - let walletCurrency = walletInfo && walletInfo.wallet_currency; - - // Item currency is formatted as 20XX for most currencies where XX is the account currency - if (walletCurrency && walletCurrency < 2000) { - walletCurrency += 2000; - } - - if (listingInfo && listingInfo.converted_price && listingInfo.converted_currencyid === walletCurrency) { - vars.price = (listingInfo.converted_price + listingInfo.converted_fee) / 100; - } - - if (!isInventoryPage()) { - // Check to see if there is a filter match - let filterColour = await filters.getMatchColour(vars); - - if (filterColour) { - const textColour = pickTextColour(filterColour, '#8F98A0', '#484848'); - floatDiv.parentNode.parentNode.style.backgroundColor = filterColour; - floatDiv.style.color = textColour; - } - } - } -}; - -// Puts all of the available items on the page into the queue for float retrieval -const getAllFloats = function() { - retrieveListingInfoFromPage().then(steamListingData => { - // Get all current items on the page (in proper order) - let listingRows = document.querySelectorAll('#searchResultsRows .market_listing_row.market_recent_listing_row'); - - for (let row of listingRows) { - // Check if we already fetched the float or if it is currently being fetched - const itemFloat = row.querySelector('.csgofloat-itemfloat'); - if (itemFloat && (itemFloat.innerText.length > 0 || row.querySelector('#getFloatBtn span').fetching)) { - continue; - } - - let id = row.id.replace('listing_', ''); - - let listingData = steamListingData[id]; - - if (!listingData.asset.market_actions?.length) { - continue; - } - - let inspectLink = listingData.asset.market_actions[0].link - .replace('%listingid%', id) - .replace('%assetid%', listingData.asset.id); - - queue.addJob(inspectLink, id); - } - }); -}; - -const sortByFloat = function() { - const listingRows = document.querySelectorAll('#searchResultsRows .market_listing_row.market_recent_listing_row'); - - document.querySelector('#csgofloat_sort_by_float span').textContent = `Sort by Float ${sortTypeAsc ? '▲' : '▼'}`; - - const items = {}; - - for (const row of listingRows) { - const id = row.id.replace('listing_', ''); - - if (floatData[id] && floatData[id].floatvalue) { - items[id] = floatData[id]; - } - } - - const sortAsc = (a, b) => items[a].floatvalue - items[b].floatvalue; - const sortDesc = (a, b) => items[b].floatvalue - items[a].floatvalue; - - // Only items that have floats fetched - const sortedItems = Object.keys(items).sort(sortTypeAsc ? sortAsc : sortDesc); - - let lastItem = document.querySelector('#searchResultsRows .market_listing_table_header'); - - for (const itemId of sortedItems) { - const itemElement = document.querySelector(`#listing_${itemId}`); - const newElem = itemElement.parentNode.insertBefore(itemElement, lastItem.nextSibling); - lastItem = newElem; - } - - sortTypeAsc = !sortTypeAsc; -}; - -const getSavedPageSize = function() { - return new Promise((resolve, reject) => { - const storageType = chrome.storage.sync ? chrome.storage.sync : chrome.storage.local; - - storageType.get(['pageSize'], size => { - resolve(size && size.pageSize); - }); - }); -}; - -const savePageSize = function(size) { - const storageType = chrome.storage.sync ? chrome.storage.sync : chrome.storage.local; - storageType.set({ pageSize: size }); -}; - -const getPageMarketHashName = async function() { - const assets = await retrieveListingAssets(); - const defaultName = document.querySelector('.market_listing_item_name').innerText; - - try { - // Attempts to retrieve the english market hash name regardless of the page language - const assetId = Object.keys(assets)[0]; - return assets[assetId]['market_hash_name']; - } catch (e) { - return defaultName; - } -}; - -// Adds float utilities -const addFloatUtilities = async function() { - let parentDiv = document.createElement('div'); - parentDiv.id = 'floatUtilities'; - - let sortByFloatsButton = createButton('Sort by Float', 'green'); - sortByFloatsButton.id = 'csgofloat_sort_by_float'; - sortByFloatsButton.addEventListener('click', sortByFloat); - parentDiv.appendChild(sortByFloatsButton); - - let savedPageSize = await getSavedPageSize(); - if (!savedPageSize) savedPageSize = 10; - - // Create page size dropdown - const pageSize = document.createElement('select'); - pageSize.id = 'pageSize'; - - const option = document.createElement('option'); - option.innerText = 'Per Page'; - option.setAttribute('disabled', ''); - pageSize.appendChild(option); - - for (const i of [10, 25, 50, 100]) { - const option = document.createElement('option'); - option.innerText = i; - option.value = i; - - if (i === savedPageSize) { - option.setAttribute('selected', ''); - } - - pageSize.appendChild(option); - } - - pageSize.addEventListener('change', e => { - const newSize = parseInt(e.srcElement.value); - window.postMessage( - { - type: 'changePageSize', - pageSize: newSize - }, - '*' - ); - savePageSize(newSize); - }); - - parentDiv.appendChild(pageSize); - - // Change the page size on first load - if (savedPageSize && savedPageSize !== 10) { - window.postMessage( - { - type: 'changePageSize', - pageSize: savedPageSize - }, - '*' - ); - } - - // Add github link - const csgofloatLink = document.createElement('a'); - csgofloatLink.classList.add('float-github'); - csgofloatLink.href = 'https://csgofloat.com'; - csgofloatLink.innerText = 'Powered by CSGOFloat'; - parentDiv.appendChild(csgofloatLink); - - // Add filter div - filters.addFilterUI(parentDiv); - - document.querySelector('#searchResultsTable').insertBefore(parentDiv, document.querySelector('#searchResultsRows')); - - const itemName = await getPageMarketHashName(); - const data = await sendMessage({ name: itemName, price: true }); - - if (!data.banner?.enable) { - return; - } - - let banner; - - if (data.banner?.dynamic) { - banner = await constructDynamicBanner(data.price, data.banner); - } else { - banner = await constructBannerImage(data.banner); - } - - document - .querySelector('#searchResultsTable') - .insertBefore(banner, document.querySelector('#searchResultsRows')); -}; - -const removeInventoryMods = function(parent) { - const floatDivs = parent.querySelectorAll('div[id*="floatdiv"]'); - - for (const div of floatDivs) { - div.parentElement.removeChild(div); - } - - const expiry = parent.querySelector('#csgofloat-owner-description'); - if (expiry) { - expiry.parentElement.removeChild(expiry); - } -}; - -const getAssetUntradableExpiry = function(assetId) { - if (!inventory.success) return; - - const assetDetails = inventory.rgInventory && inventory.rgInventory[assetId]; - if (!assetDetails) return; - - const description = - inventory.rgDescriptions && inventory.rgDescriptions[`${assetDetails.classid}_${assetDetails.instanceid}`]; - if (!description) return; - - if (!description.tradable) { - return description.cache_expiration; - } -}; - -const addInventoryMods = async function(boxContent) { - removeInventoryMods(boxContent); - - // Get the inspect link - const inspectButton = boxContent.querySelector('div.item_actions a.btn_small'); - - if (!inspectButton || !extractInspectAssetId(inspectButton.href)) { - return; - } - - const inspectLink = inspectButton.href; - const id = extractInspectAssetId(inspectLink); - const steamId = extractInspectSteamId(inspectLink); - - // Check if we already placed the button - if (boxContent.querySelector(`#item_${id}_floatdiv`)) { - return; - } - - // Check if this is a weapon - const description = await retrieveInventoryItemDescription(id); - if ( - !description || - !description.tags.find( - a => a.category === 'Weapon' || (a.category === 'Type' && a.internal_name === 'Type_Hands') - ) - ) { - return; - } - - const floatDiv = document.createElement('div'); - floatDiv.style.marginBottom = '10px'; - floatDiv.id = `item_${id}_floatdiv`; - - const gameInfo = boxContent.querySelector('.item_desc_game_info'); - gameInfo.parentElement.insertBefore(floatDiv, gameInfo.nextSibling); - - const getFloatButton = createButton('Fetching...', 'green', 'getFloatBtn'); - getFloatButton.addEventListener('click', () => { - queue.addJob(inspectLink, id, /* force */ true); - }); - getFloatButton.inspectLink = inspectLink; - floatDiv.appendChild(getFloatButton); - - // Create divs the following class names and append them to the button div - for (let className of ['floatmessage', 'csgofloat-itemfloat', 'csgofloat-itemseed']) { - let div = document.createElement('div'); - div.classList.add(className); - floatDiv.appendChild(div); - } - - // Check if this item is not tradable and if we can figure out when it expires - // This currently only works for weapons - const expires = getAssetUntradableExpiry(id); - const isOwner = - boxContent.querySelector('#iteminfo0_item_owner_descriptors') || - boxContent.querySelector('#iteminfo1_item_owner_descriptors'); - - stallFetcher.getStallItem(steamId, id).then(async e => { - if (!e) { - const owner = await isInventoryOwner(); - if (owner) { - const listCSGOFloat = createButton('List on CSGOFloat', 'green'); - listCSGOFloat.addEventListener('click', () => { - window.open('https://csgofloat.com', '_blank'); - }); - floatDiv.appendChild(listCSGOFloat); - } - - return; - } - - const wrap = document.createElement('div'); - wrap.style.padding = '5px'; - wrap.style.width = 'fit-content'; - wrap.style.border = '1px #5a5a5a solid'; - wrap.style.backgroundColor = '#383838'; - wrap.style.borderRadius = '3px'; - - - const elem = document.createElement('a'); - elem.href = `https://csgofloat.com/item/${e.id}`; - elem.style.fontSize = '15px'; - elem.style.display = 'flex'; - elem.style.alignItems = 'center'; - elem.target = '_blank'; - - const logo = document.createElement('img'); - logo.src = 'https://csgofloat.com/assets/full_logo.png'; - logo.height = 21; - elem.appendChild(logo); - - const txt = document.createElement('span'); - txt.style.marginLeft = '5px'; - txt.innerText = `Listed for $${(e.price / 100).toFixed(2)}`; - - elem.appendChild(txt); - wrap.appendChild(elem); - - floatDiv.appendChild(wrap); - }); - - if (expires && isOwner.style.display === 'none') { - const tagDiv = - boxContent.querySelector('#iteminfo0_item_tags') || boxContent.querySelector('#iteminfo1_item_tags'); - - const descriptionParent = document.createElement('div'); - descriptionParent.classList.add('item_desc_descriptors'); - descriptionParent.id = 'csgofloat-owner-description'; - - const descriptor = document.createElement('div'); - descriptor.classList.add('descriptor'); - descriptor.style.color = 'rgb(255, 64, 64)'; - descriptor.innerText = 'Tradable After ' + new Date(expires).toGMTString(); - - const descriptorBreak = document.createElement('div'); - descriptorBreak.classList.add('descriptor'); - descriptorBreak.innerHTML = ' '; - - descriptionParent.appendChild(descriptorBreak); - descriptionParent.appendChild(descriptor); - - tagDiv.parentElement.insertBefore(descriptionParent, tagDiv); - } - - // Check if we already have the float for this item - if (id in floatData) { - showFloat(id); - } else { - queue.addJob(inspectLink, id); - } -}; - -// Adds float boxes to inventory pages -const addInventoryBoxes = async function() { - let owner; - if (isInventoryPage()) { - owner = await retrieveInventoryOwner(); - } - - for (const page of document.querySelectorAll('.inventory_page')) { - if (isTradePage()) { - owner = page.parentNode.id.replace("inventory_", "").replace("_730_2", ""); - } - - // Don't include non-visible pages - if (page.style.display === 'none') { - continue; - } - - for (const itemHolder of page.querySelectorAll('.itemHolder')) { - const item = itemHolder.querySelector('div.item.app730'); - if (!item) continue; - const assetId = item.id.split('_')[2]; // TODO: Error check? - - const description = await retrieveInventoryItemDescription(assetId); - if ( - !description || - !description.tags.find( - a => a.category === 'Weapon' || (a.category === 'Type' && a.internal_name === 'Type_Hands') - ) - ) { - continue; - } - - if (!item.querySelector(`#item_${assetId}_floatdiv`)) { - const s = document.createElement('span'); - s.id = `item_${assetId}_floatdiv`; - s.minimal = true; - - const floatSpan = document.createElement('span'); - floatSpan.style.position = 'absolute'; - floatSpan.style.bottom = '3px'; - floatSpan.style.right = '3px'; - floatSpan.style.fontSize = '12px'; - floatSpan.classList.add('csgofloat-itemfloat'); - - const seedSpan = document.createElement('span'); - seedSpan.style.position = 'absolute'; - seedSpan.style.top = '3px'; - seedSpan.style.right = '3px'; - seedSpan.style.fontSize = '12px'; - seedSpan.classList.add('csgofloat-itemseed'); - - // Adjust styling for users who also use steam inventory helper - if (item.querySelector('.p-price')) { - floatSpan.style.top = '3px'; - floatSpan.style.bottom = ''; - seedSpan.style.top = '17px'; - } - - s.appendChild(floatSpan); - s.appendChild(seedSpan); - - item.appendChild(s); - } - - const inspectLink = description.actions[0].link - .replace('%owner_steamid%', owner) - .replace('%assetid%', assetId); - - // If we don't already have data fetched - if (!item.querySelector('.csgofloat-itemfloat').innerText) { - if (assetId in floatData) { - showFloat(assetId); - } else { - queue.addJob(inspectLink, assetId); - } - } - } - } -}; - -// If an item on the current page doesn't have the float div/buttons, this function adds it -const addMarketButtons = async function() { - // Iterate through each item on the page - let listingRows = document.querySelectorAll('.market_listing_row.market_recent_listing_row'); - - for (let row of listingRows) { - let id = row.id.replace('listing_', '').replace('Copy', ''); - const steamListingData = await retrieveListingInfoFromPage(id); - const listingData = steamListingData[id]; - - if (!listingData || !listingData.asset.market_actions) continue; - - if (row.querySelector(`#item_${id}_floatdiv`)) { - continue; - } - - // Check if a 'buylisting' hash fragment can now be matched - checkMarketHash(); - - const inspectLink = listingData.asset.market_actions[0].link - .replace('%listingid%', id) - .replace('%assetid%', listingData.asset.id); - - let listingNameElement = row.querySelector(`#listing_${id}_name`); - if (!listingNameElement) { - // Handle instances where it's a buy dialog opening - listingNameElement = row.querySelector(`#listing_${id}_nameCopy`); - } - - let floatDiv = document.createElement('div'); - floatDiv.classList.add('float-div'); - floatDiv.id = `item_${id}_floatdiv`; - listingNameElement.parentElement.appendChild(floatDiv); - - // Create divs the following class names and append them to the button div - let divClassNames = ['floatmessage', 'csgofloat-itemfloat', 'csgofloat-itemseed']; - - for (let className of divClassNames) { - let div = document.createElement('div'); - div.classList.add(className); - floatDiv.appendChild(div); - } - - let getFloatButton = createButton('Get Float', 'green', 'getFloatBtn'); - getFloatButton.addEventListener('click', () => { - queue.addJob(inspectLink, id, /* force */ true); - }); - getFloatButton.style.marginRight = '10px'; - floatDiv.appendChild(getFloatButton); - - let fetchingModel = false; - const modelButton = createButton('CS.Money 3D', 'green'); - modelButton.style.marginRight = '10px'; - modelButton.addEventListener('click', async () => { - if (fetchingModel) return; - - // Makes iframe togglable - const existingFrame = floatDiv.parentNode.parentNode.querySelector('.float-model-frame'); - if (existingFrame) { - existingFrame.parentNode.removeChild(existingFrame); - return; - } - - // If screenshot open, remove it - const existingScreenshot = floatDiv.parentNode.parentNode.querySelector('.float-screenshot-frame'); - if (existingScreenshot) { - existingScreenshot.parentNode.removeChild(existingScreenshot); - } - - fetchingModel = true; // prevent from repeatedly clicking the button - modelButton.querySelector('span').innerText = 'Fetching 3D Model...'; - - const hangOn = setTimeout(() => { - modelButton.querySelector('span').innerText = 'Fetching 3D Model...hang on...'; - }, 5000); - - const data = await sendMessage({ inspectLink, model: true }); - clearTimeout(hangOn); - fetchingModel = false; - modelButton.querySelector('span').innerText = 'CS.Money 3D'; - - if (data.modelLink) { - const iframe = document.createElement('iframe'); - iframe.src = chrome.runtime.getURL('model_frame.html') + '?url=' + encodeURIComponent(data.modelLink); - iframe.classList.add('float-model-frame'); - floatDiv.parentNode.parentNode.appendChild(iframe); - } else if (data.error) { - alert(data.error); - } - }); - floatDiv.appendChild(modelButton); - - let fetchingScreenshot = false; - const screenshotButton = createButton('Screenshot', 'green'); - screenshotButton.addEventListener('click', async () => { - if (fetchingScreenshot) return; - - // Makes screenshot togglable - const existingScreenshot = floatDiv.parentNode.parentNode.querySelector('.float-screenshot-frame'); - if (existingScreenshot) { - existingScreenshot.parentNode.removeChild(existingScreenshot); - return; - } - - // If 3D view is open, remove it - const existingFrame = floatDiv.parentNode.parentNode.querySelector('.float-model-frame'); - if (existingFrame) { - existingFrame.parentNode.removeChild(existingFrame); - } - - fetchingScreenshot = true; // prevent from repeatedly clicking the button - screenshotButton.querySelector('span').innerText = 'Fetching Screenshot...'; - - const hangOn = setTimeout(() => { - screenshotButton.querySelector('span').innerText = 'Fetching Screenshot...hang on...'; - }, 5000); - - const data = await sendMessage({ inspectLink, model: true }); - clearTimeout(hangOn); - fetchingScreenshot = false; - screenshotButton.querySelector('span').innerText = 'Screenshot'; - - if (data.screenshotLink) { - const img = document.createElement('img'); - img.src = data.screenshotLink; - img.classList.add('float-screenshot-frame'); - floatDiv.parentNode.parentNode.appendChild(img); - } else if (data.error) { - alert(data.error); - } - }); - floatDiv.appendChild(screenshotButton); - - const assetID = listingData.asset.id; - const steamListingAssets = await retrieveListingAssets(assetID); - - // Show inline stickers - const asset = steamListingAssets[assetID]; - const lastDescription = asset.descriptions[asset.descriptions.length - 1]; - if (lastDescription.type === 'html' && lastDescription.value.includes('sticker')) { - const imagesHtml = lastDescription.value.match(/()/g); - const nameMatch = lastDescription.value.match(/
([^<].*?): (.*)<\/center>/); - - if (nameMatch) { - const stickerLang = nameMatch[1]; - const stickerNames = nameMatch[2].split(', '); - - // Adds href link to sticker - let resHtml = ''; - for (let i = 0; i < imagesHtml.length; i++) { - const url = - stickerLang === 'Sticker' - ? `https://steamcommunity.com/market/listings/730/${stickerLang} | ${stickerNames[i]}` - : `https://steamcommunity.com/market/search?q=${stickerLang} | ${stickerNames[i]}`; - - resHtml += ` - ${imagesHtml[i]} - - `; - } - - const imgContainer = document.createElement('div'); - imgContainer.classList.add('float-stickers-container'); - imgContainer.innerHTML = resHtml; - const itemNameBlock = row.querySelector('.market_listing_item_name_block'); - itemNameBlock.insertBefore(imgContainer, itemNameBlock.firstChild); - } - } - - // Easy inspect link (only if they don't have SIH) - if (!row.querySelector('.sih-inspect-magnifier')) { - const imageContainer = row.querySelector('.market_listing_item_img_container'); - const easyLink = document.createElement('a'); - easyLink.href = inspectLink; - easyLink.innerText = '🔍'; - easyLink.classList.add('easy-inspect'); - - imageContainer.appendChild(easyLink); - } - - // Remove Steam Inventory Helper Stickers (conflicts with ours) - const sihStickers = row.querySelector('.sih-images'); - if (sihStickers) { - sihStickers.parentElement.removeChild(sihStickers); - } - - // check if we already have the float for this item - if (id in floatData) { - showFloat(id); - } - } - - // Add float utilities if it doesn't exist and we have valid items - if (!floatUtilitiesAdded && listingRows.length > 0) { - floatUtilitiesAdded = true; - addFloatUtilities(); - } - - // Automatically retrieve all floats - getAllFloats(); -}; - -const getStorageVersion = async function(storageType) { - return new Promise((resolve, reject) => { - storageType.get(['version'], items => { - resolve(items['version'] || '0.0.0'); - }); - }); -}; - -const migrateStorage = async function() { - let storageType = chrome.storage.sync; - if (!storageType) storageType = chrome.storage.local; - - const storageVersion = await getStorageVersion(storageType); - - if (versionCompare(storageVersion, '1.2.2') === -1) { - // storageVersion < 1.2.2 - console.log('Migrating storage to 1.2.2'); - storageType.get(null, items => { - // Want to remove all keys that are empty arrays - // #20 - const keys = Object.keys(items); - const emptyKeys = []; - - for (const key of keys) { - if (Array.isArray(items[key]) && items[key].length === 0) { - emptyKeys.push(key); - } - } - - storageType.remove(emptyKeys); - }); - } - - storageType.set({ version }); -}; - -const TargetMutationObserver = function(target, cb) { - return new MutationObserver(() => { - cb(target); - }).observe(target, { childList: true, subtree: true }); -}; - - -async function main() { - migrateStorage(); - queue.start(); - - walletInfo = await retrieveWalletInfo(); - - if (isTradePage()) { - addFloatMarketFill(); - setInterval(() => { - addInventoryBoxes(); - }, 250); - } else if (isInventoryPage()) { - retrieveInventoryOwner().then(async ownerId => { - // We have to request the inventory from a separate endpoint that includes untradable expiration - inventory = await sendMessage({ steamId: ownerId, inventory: true }); - - const action0 = document.querySelector('#iteminfo0_item_actions'); - const action1 = document.querySelector('#iteminfo1_item_actions'); - - // Page uses two divs that interchange with another on item change - TargetMutationObserver(action0, t => addInventoryMods(t.parentElement.parentElement)); - TargetMutationObserver(action1, t => addInventoryMods(t.parentElement.parentElement)); - - // Ensure we catch the first item div on page load - addInventoryMods(action1.parentElement.parentElement); - - setInterval(() => { - addInventoryBoxes(); - }, 250); - }); - } else { - setInterval(() => { - addMarketButtons(); - }, 250); - } -} - -main(); - -const logStyle = 'background: #222; color: #fff;'; -console.log(`%c CSGOFloat Market Checker (v${version}) by Step7750 `, logStyle); -console.log('%c Changelog can be found here: https://github.com/Step7750/CSGOFloat-Extension ', logStyle); diff --git a/lib/banner.js b/lib/banner.js deleted file mode 100644 index 28a48526..00000000 --- a/lib/banner.js +++ /dev/null @@ -1,59 +0,0 @@ -const constructDynamicBanner = async function(skinPrice, props) { - const csmoneyDiv = document.createElement('div'); - csmoneyDiv.id = 'floatBanner'; - csmoneyDiv.style.padding = '12px 10px 12px 10px'; - - const moneyButton = document.createElement('a'); - const moneyLogo = document.createElement('img'); - moneyLogo.src = 'https://cs.money/svg/logo.svg'; - moneyLogo.height = 32; - - const staticText = document.createElement('span'); - staticText.innerText = 'Get this skin on '; - staticText.style.verticalAlign = 'bottom'; - - const priceText = document.createElement('span'); - const price = document.createElement('span'); - price.innerText = `$${(skinPrice || 0).toFixed(2)}`; - price.style.fontWeight = 'bold'; - price.style.verticalAlign = 'bottom'; - - priceText.appendChild(price); - priceText.insertAdjacentText('afterbegin', ' for '); - priceText.insertAdjacentText('beforeend', ' USD'); - - priceText.style.verticalAlign = 'bottom'; - - if (!skinPrice) { - priceText.innerText = ''; - } - - moneyButton.appendChild(staticText); - moneyButton.appendChild(moneyLogo); - moneyButton.appendChild(priceText); - moneyButton.classList.add('float-money-button'); - csmoneyDiv.appendChild(moneyButton); - - moneyButton.target = '_blank'; - moneyButton.href = props.link || 'https://cs.money/?utm_source=sponsorship&utm_medium=csgoflt&utm_campaign=csgofloat&utm_content=link'; - - return csmoneyDiv; -} - -const constructBannerImage = async function(props) { - const bannerDiv = document.createElement('div'); - bannerDiv.id = 'floatBanner'; - - const link = document.createElement('a'); - link.href = props.link; - link.target = '_blank'; - - const bannerLogo = document.createElement('img'); - bannerLogo.src = props.src; - bannerLogo.height = props.height; - - link.appendChild(bannerLogo); - bannerDiv.appendChild(link); - - return bannerDiv; -} diff --git a/lib/bridge.js b/lib/bridge.js deleted file mode 100644 index 16093b99..00000000 --- a/lib/bridge.js +++ /dev/null @@ -1,265 +0,0 @@ -let steamListingInfo = {}; -let steamListingAssets = {}; -let listingInfoPromises = []; -let listingAssetPromises = []; -let inventoryItemRequests = []; -let inventoryOwnerRequests = []; -let isInventoryOwnerRequests = []; -let walletInfoRequests = []; -let partnerInfoRequests = []; -let tradeStatusRequests = []; - -// retrieve g_rgListingInfo from page script -window.addEventListener('message', e => { - if (e.data.type === 'listingInfo') { - steamListingInfo = e.data.listingInfo; - - // resolve listingInfoPromises - for (let promise of listingInfoPromises) promise(steamListingInfo); - - listingInfoPromises = []; - } else if (e.data.type === 'inventoryItemDescription') { - const unfulfilledRequests = []; - - for (const request of inventoryItemRequests) { - if (request.assetId === e.data.assetId) { - request.promise(e.data.description); - } else { - unfulfilledRequests.push(request); - } - } - - inventoryItemRequests = unfulfilledRequests; - } else if (e.data.type === 'listingAssets') { - steamListingAssets = e.data.assets[730][2]; - for (const promise of listingAssetPromises) promise(steamListingAssets); - } else if (e.data.type === 'inventoryOwner') { - for (const promise of inventoryOwnerRequests) promise(e.data.owner); - } else if (e.data.type === 'walletInfo') { - for (const promise of walletInfoRequests) promise(e.data.wallet); - } else if (e.data.type === 'partnerInfo') { - for (const promise of partnerInfoRequests) promise(e.data.partner); - } else if (e.data.type === 'isInventoryOwner') { - for (const promise of isInventoryOwnerRequests) promise(e.data.isOwner); - } else if (e.data.type === 'tradeStatus') { - for (const promise of tradeStatusRequests) promise(e.data.tradeStatus); - } -}); - -const retrieveListingInfoFromPage = function(listingId) { - if (listingId != null && listingId in steamListingInfo) { - return Promise.resolve(steamListingInfo); - } - - window.postMessage( - { - type: 'requestListingInfo' - }, - '*' - ); - - return new Promise(resolve => { - listingInfoPromises.push(resolve); - }); -}; - -const retrieveListingAssets = function(assetId) { - if (assetId != null && assetId in steamListingAssets) { - return Promise.resolve(steamListingAssets); - } - - window.postMessage( - { - type: 'requestAssets' - }, - '*' - ); - - return new Promise(resolve => { - listingAssetPromises.push(resolve); - }); -}; - -const retrieveInventoryItemDescription = function(assetId) { - window.postMessage( - { - type: 'requestInventoryItemDescription', - assetId - }, - '*' - ); - - return new Promise(resolve => { - inventoryItemRequests.push({ promise: resolve, assetId }); - }); -}; - -const retrieveInventoryOwner = function() { - window.postMessage( - { - type: 'requestInventoryOwner' - }, - '*' - ); - - return new Promise(resolve => { - inventoryOwnerRequests.push(resolve); - }); -}; - -const isInventoryOwner = function() { - window.postMessage( - { - type: 'requestIsInventoryOwner' - }, - '*' - ); - - return new Promise(resolve => { - isInventoryOwnerRequests.push(resolve); - }); -}; - -const retrieveWalletInfo = function() { - window.postMessage( - { - type: 'requestWalletInfo' - }, - '*' - ); - - return new Promise(resolve => { - walletInfoRequests.push(resolve); - }); -}; - -const retrievePartnerInfo = function() { - window.postMessage( - { - type: 'requestPartnerInfo' - }, - '*' - ); - - return new Promise(resolve => { - partnerInfoRequests.push(resolve); - }); -}; - -const retrieveTradeStatus = function() { - window.postMessage( - { - type: 'requestTradeStatus' - }, - '*' - ); - - return new Promise(resolve => { - tradeStatusRequests.push(resolve); - }); -}; - -const checkMarketHash = function() { - window.postMessage( - { - type: 'checkMarketHash' - }, - '*' - ); -}; - -// register the message listener in the page scope -let script = document.createElement('script'); -script.innerText = ` - window.csgofloat = true; - - window.addEventListener('message', (e) => { - if (e.data.type == 'requestListingInfo') { - window.postMessage({ - type: 'listingInfo', - listingInfo: g_rgListingInfo - }, '*'); - } else if (e.data.type == 'requestAssets') { - window.postMessage({ - type: 'listingAssets', - assets: g_rgAssets - }, '*'); - } else if (e.data.type == 'requestInventoryItemDescription') { - if (g_ActiveInventory.m_rgAssets) { - const asset = g_ActiveInventory.m_rgAssets[e.data.assetId]; - - if (!asset) { - window.postMessage({ - type: 'inventoryItemDescription', - assetId: e.data.assetId, - }, '*'); - return; - } - - const key = asset.instanceid == "0" ? asset.classid : asset.classid + '_' + asset.instanceid; - const description = g_ActiveInventory.m_rgDescriptions[key]; - - window.postMessage({ - type: 'inventoryItemDescription', - assetId: e.data.assetId, - description - }, '*'); - } else { - /* Trade inventory */ - const asset = g_ActiveInventory.rgInventory[e.data.assetId]; - if (!asset) { - window.postMessage({ - type: 'inventoryItemDescription', - assetId: e.data.assetId, - }, '*'); - return; - } - - /* delete HTML elements */ - const clone = { ...asset }; - delete clone.element; - delete clone.homeElement; - - window.postMessage({ - type: 'inventoryItemDescription', - assetId: e.data.assetId, - description: clone - }, '*'); - } - } else if (e.data.type == 'changePageSize') { - g_oSearchResults.m_cPageSize = e.data.pageSize; - g_oSearchResults.GoToPage(0, true); - } else if (e.data.type == 'requestInventoryOwner') { - window.postMessage({ - type: 'inventoryOwner', - owner: g_ActiveInventory.m_owner.strSteamId - }, '*'); - } else if (e.data.type == 'requestWalletInfo') { - window.postMessage({ - type: 'walletInfo', - wallet: typeof g_rgWalletInfo !== 'undefined' && g_rgWalletInfo - }, '*'); - } else if (e.data.type == 'requestPartnerInfo') { - window.postMessage({ - type: 'partnerInfo', - partner: g_ulTradePartnerSteamID - }, '*'); - } else if (e.data.type == 'requestIsInventoryOwner') { - window.postMessage({ - type: 'isInventoryOwner', - isOwner: g_ActiveInventory.m_owner.strSteamId === g_steamID - }, '*'); - } else if (e.data.type == 'requestTradeStatus') { - window.postMessage({ - type: 'tradeStatus', - tradeStatus: g_rgCurrentTradeStatus - }, '*'); - } else if (e.data.type == 'checkMarketHash') { - if (!BuyItemDialog.m_bInitialized) { - MarketCheckHash(); - } - } - }); -`; - -document.head.appendChild(script); diff --git a/lib/filters.js b/lib/filters.js deleted file mode 100644 index c6a7ce7f..00000000 --- a/lib/filters.js +++ /dev/null @@ -1,559 +0,0 @@ -class Filter { - constructor(expression, colour, isGlobal, filters) { - this.expression = expression; - this.colour = colour; - this.isGlobal = isGlobal; - this.validExpressionVars = ['float', 'seed', 'minfloat', 'maxfloat', 'minwearfloat', 'maxwearfloat', 'phase']; - this.filters = filters; - - this.compileExpression(); - } - - static filtrexMatch(str, reg) { - let thisMatch = str.toString().match(reg); - - if (thisMatch !== null) return thisMatch.length; - else return 0; - } - - static percentile(vars) { - const minFloat = vars.minfloat > vars.minwearfloat ? vars.minfloat : vars.minwearfloat; - const maxFloat = vars.maxfloat < vars.maxwearfloat ? vars.maxfloat : vars.maxwearfloat; - const itemPercentile = 100 - (100 * (vars.float - minFloat)) / (maxFloat - minFloat); - - return function(rank) { - // Assumes floats are distributed evenly - return itemPercentile > rank ? 1 : 0; - }; - } - - static percentileRange(vars) { - const minFloat = vars.minfloat > vars.minwearfloat ? vars.minfloat : vars.minwearfloat; - const maxFloat = vars.maxfloat < vars.maxwearfloat ? vars.maxfloat : vars.maxwearfloat; - const itemPercentile = 100 - (100 * (vars.float - minFloat)) / (maxFloat - minFloat); - - return function(minRank, maxRank) { - // Assumes floats are distributed evenly - return itemPercentile > minRank && itemPercentile < maxRank ? 1 : 0; - }; - } - - func(vars) { - return this.filtrexFunc(vars, { - match: Filter.filtrexMatch, - percentile: Filter.percentile(vars), - percentileRange: Filter.percentileRange(vars) - }); - } - - compileExpression() { - this.filtrexFunc = compileExpression(this.expression, this.validExpressionVars); - } - - onFilterColourChange(e) { - let colourSwitch = e.target || e.srcElement; - - this.filters.setFilterColour(this, colourSwitch.value); - } - - addToUI() { - let parentDiv = document.querySelector('#floatFilters'); - - let thisDiv = document.createElement('div'); - thisDiv.innerText = this.expression; - - let colourDiv = document.createElement('input'); - colourDiv.type = 'color'; - colourDiv.value = this.colour; - colourDiv.style.float = 'left'; - colourDiv.style.marginRight = '10px'; - colourDiv.style.marginTop = '-3px'; - colourDiv.addEventListener('change', e => this.onFilterColourChange(e)); - thisDiv.appendChild(colourDiv); - - // Add remove filter btn - let removeFilterBtn = createButton('Remove Filter', 'grey'); - removeFilterBtn.addEventListener('click', e => this.removeFilter(e)); - removeFilterBtn.style.marginTop = '-3px'; - removeFilterBtn.style.float = 'right'; - thisDiv.appendChild(removeFilterBtn); - - // Add global filter toggle btn - let globalToggleBtn = createButton('Global', this.isGlobal ? 'green' : 'grey'); - globalToggleBtn.addEventListener('click', e => { - globalToggleBtn.classList.remove(`btn_${this.isGlobal ? 'green' : 'grey'}_white_innerfade`); - this.isGlobal = !this.isGlobal; - globalToggleBtn.classList.add(`btn_${this.isGlobal ? 'green' : 'grey'}_white_innerfade`); - this.filters.saveFilters(); - }); - globalToggleBtn.classList.add('float-tooltip'); - globalToggleBtn.style.marginTop = '-3px'; - globalToggleBtn.style.marginRight = '10px'; - globalToggleBtn.style.float = 'right'; - - // Inner tooltip text - const tooltipText = document.createElement('span'); - tooltipText.classList.add('tiptext'); - tooltipText.innerText = 'Global filters apply to every item on the market'; - tooltipText.style.background = 'darkslategrey'; - globalToggleBtn.appendChild(tooltipText); - thisDiv.appendChild(globalToggleBtn); - - // Add line break - let hr = document.createElement('hr'); - thisDiv.appendChild(hr); - - this.div = thisDiv; - parentDiv.appendChild(thisDiv); - } - - removeFilter() { - this.filters.removeFilter(this); - } -} - -class Filters { - constructor() { - this.filters = []; - this.expressionTimer = false; - this.waitForFilters = []; - this.filtersLoaded = false; - } - - onFiltersLoaded() { - if (this.filtersLoaded) { - return Promise.resolve(); - } else { - return new Promise((resolve) => { - this.waitForFilters.push(resolve); - }) - } - } - - async getMatchColour(vars) { - // Ensure that filters are loaded from storage - await this.onFiltersLoaded(); - - let colours = []; - - for (let filter of this.filters) { - if (filter.func(vars) == true) colours.push(hexToRgb(filter.colour)); - } - - if (colours.length > 0) { - // Get the average colour between each matching filter - let avg_colours = [0, 0, 0]; - - for (let colour of colours) { - for (let index in colour) { - avg_colours[index] += colour[index]; - } - } - - for (let index in avg_colours) { - avg_colours[index] = parseInt(avg_colours[index] / colours.length); - } - - return rgbToHex(avg_colours); - } - } - - addFilter(expression, colour) { - if (arguments.length === 0) { - expression = document.querySelector('#float_expression_filter').value; - colour = document.querySelector('#floatFilterColour').value; - } - - let filter = new Filter(expression, colour, false, this); - - filter.addToUI(); - - this.filters.push(filter); - this.saveFilters(); - - // Reset expression input value - document.querySelector('#float_expression_filter').value = ''; - } - - tryCompile(expression) { - new Filter(expression, '', false, this); - } - - setFilterColour(filter, colour) { - let index = this.filters.indexOf(filter); - - if (index === -1) return; - - this.filters[index].colour = colour; - - this.saveFilters(); - } - - removeFilter(filter) { - let index = this.filters.indexOf(filter); - - if (index === -1) return; - - filter.div.parentNode.removeChild(filter.div); - this.filters.splice(index, 1); - - this.saveFilters(); - } - - onHelpClick() { - let filterdiv = document.querySelector('#floatFilter'); - - let helpdiv = filterdiv.querySelector('#filterHelp'); - if (helpdiv) filterdiv.removeChild(helpdiv); - else { - // create it - helpdiv = document.createElement('div'); - helpdiv.id = 'filterHelp'; - - helpdiv.innerHTML = ` -
- Filters will highlight matching items with the specified colour

- - Note: If multiple filters match an item, it will be highlighted with the average colour

- - New: You can now filter based on FloatDB ranks and item price!

- - Examples: - - - Variables - - - Functions: - - `; - - filterdiv.appendChild(helpdiv); - } - } - - async addFilterUI(parent) { - let filterdiv = document.createElement('div'); - filterdiv.id = 'floatFilter'; - parent.appendChild(filterdiv); - - // Add separator - let hr = document.createElement('hr'); - filterdiv.appendChild(hr); - - // Adds filters div - let filtersdiv = document.createElement('div'); - filtersdiv.id = 'floatFilters'; - filterdiv.appendChild(filtersdiv); - - // Adds colour picker - let colourDiv = document.createElement('input'); - colourDiv.id = 'floatFilterColour'; - colourDiv.type = 'color'; - colourDiv.value = '#354908'; - colourDiv.style.float = 'left'; - colourDiv.style.marginTop = '2px'; - filterdiv.appendChild(colourDiv); - - // Add new filter input box - let input = document.createElement('input'); - input.id = 'float_expression_filter'; - input.classList.add('filter_search_box'); - input.placeholder = 'Add Highlight Filter'; - input.style.width = '350px'; - input.style.marginLeft = '10px'; - input.addEventListener('keyup', e => this.filterKeyPress(e)); - filterdiv.appendChild(input); - - // Add filter help link - let helpText = document.createElement('a'); - helpText.innerText = 'ⓘ'; - helpText.style.fontSize = '18px'; - helpText.title = 'Filter Help'; - helpText.style.marginLeft = '5px'; - helpText.href = 'javascript:void(0)'; - helpText.addEventListener('click', e => this.onHelpClick(e)); - filterdiv.appendChild(helpText); - - // Add compile status indicator - let status = document.createElement('div'); - status.id = 'compileStatus'; - filterdiv.appendChild(status); - - // Add new filter btn - let addFilterBtn = createButton('Add Filter', 'green'); - addFilterBtn.addEventListener('click', e => this.addFilter()); - addFilterBtn.id = 'addFloatFilter'; - addFilterBtn.style.display = 'none'; - addFilterBtn.style.marginLeft = '10px'; - - filterdiv.appendChild(addFilterBtn); - - // Compile error div - let compileError = document.createElement('div'); - compileError.id = 'compileError'; - filterdiv.appendChild(compileError); - - const globalFilters = await this.getGlobalFilters(); - const localFilters = await this.getItemFilters(); - - const allFilters = globalFilters.concat(localFilters); - - for (let filter of allFilters) { - let newFilter = new Filter(filter.expression, filter.colour, !!filter.isGlobal, this); - this.filters.push(newFilter); - newFilter.addToUI(); - } - - this.filtersLoaded = true; - for (const resolve of this.waitForFilters) { - resolve(); - } - } - - filterKeyPress() { - if (this.expressionTimer) clearTimeout(this.expressionTimer); - - this.expressionTimer = setTimeout(() => { - let input = document.querySelector('#float_expression_filter'); - let compileError = document.querySelector('#compileError'); - let status = document.querySelector('#compileStatus'); - let addFilterBtn = document.querySelector('#addFloatFilter'); - - let expression = input.value; - - // try to compile the expression - try { - this.tryCompile(expression); - status.setAttribute('error', 'false'); - status.innerText = '✓'; - compileError.innerText = ''; - addFilterBtn.style.display = ''; - } catch (e) { - if (expression === '') { - status.innerText = ''; - compileError.innerText = ''; - } else { - status.setAttribute('error', 'true'); - compileError.innerText = e.message; - } - addFilterBtn.style.display = 'none'; - } - }, 250); - } - - getSaveKey() { - let itemName = document.querySelector('.market_listing_nav a:nth-child(2)'); - - if (itemName) return itemName.innerText + '_expressions'; - } - - getItemFilters() { - return new Promise((resolve, reject) => { - let key = this.getSaveKey(); - - if (!key) cb([]); - - let syncFilters = {}; - syncFilters[key] = []; - - let storageType = chrome.storage.sync; - if (!storageType) storageType = chrome.storage.local; - - storageType.get(syncFilters, items => { - resolve(items[key]); - }); - }); - } - - getGlobalFilters() { - return new Promise((resolve, reject) => { - let syncFilters = {}; - syncFilters['global'] = []; - - let storageType = chrome.storage.sync; - if (!storageType) storageType = chrome.storage.local; - - storageType.get(syncFilters, items => { - resolve(items['global']); - }); - }); - } - - /** - * Ensures we don't hit MAX_WRITE_OPERATIONS_PER_MINUTE - */ - saveFilters() { - clearTimeout(this.saveTimeout); - - this.saveTimeout = setTimeout(() => this._saveFilters(), 500); - } - - _saveFilters() { - let key = this.getSaveKey(); - - if (!key) return; - - let syncFilters = {}; - - const pureFilters = this.filters.map(f => ({ - expression: f.expression, - colour: f.colour, - isGlobal: f.isGlobal - })); - const localFilters = pureFilters.filter(f => !f.isGlobal); - const globalFilters = pureFilters.filter(f => f.isGlobal); - - syncFilters['global'] = globalFilters; - syncFilters[key] = localFilters; - - let storageType = chrome.storage.sync; - if (!storageType) storageType = chrome.storage.local; - - if (localFilters.length === 0) { - storageType.remove(key); - delete syncFilters[key]; - } - - storageType.set(syncFilters, () => { - if (chrome.runtime.lastError) { - alert( - 'Error occurred while saving, you may have to remove some filters and try again\n' + - chrome.runtime.lastError.toString() - ); - } - }); - - // update UI - removeAllItemsHtml(); - } -} diff --git a/lib/filtrex.js b/lib/filtrex.js deleted file mode 100644 index d9660db7..00000000 --- a/lib/filtrex.js +++ /dev/null @@ -1,3556 +0,0 @@ -/** - * Filtrex provides compileExpression() to compile user expressions to JavaScript. - * - * See https://github.com/joewalnes/filtrex for tutorial, reference and examples. - * MIT License. - * - * Includes Jison by Zachary Carter. See http://jison.org/ - * - * -Joe Walnes - */ -function compileExpression(expression, validVars) { - var functions = { - abs: Math.abs, - ceil: Math.ceil, - floor: Math.floor, - log: Math.log, - max: Math.max, - min: Math.min, - random: Math.random, - round: Math.round, - sqrt: Math.sqrt, - }; - if (!compileExpression.parser) { - // Building the original parser is the heaviest part. Do it - // once and cache the result in our own function. - compileExpression.parser = filtrexParser(); - } - var tree = compileExpression.parser.parse(expression); - - var js = []; - js.push('return '); - function toJs(node) { - if (Array.isArray(node)) { - node.forEach(toJs); - } else { - js.push(node); - } - } - tree.forEach(toJs); - js.push(';'); - - js = js.join(''); - - // check if each var is proper in the js - if (validVars) { - let reg = /data\[\"(.+?)\"\]/g; - let match = reg.exec(js); - - while (match !== null) { - let dataVar = match[1]; - - if (validVars.indexOf(dataVar) === -1) { - throw new Error(`'${dataVar}' is an improper variable name`); - } - - match = reg.exec(js); - } - } - - function unknown(funcName) { - throw 'Unknown function: ' + funcName + '()'; - } - - function prop(obj, name) { - return Object.prototype.hasOwnProperty.call(obj||{}, name) ? obj[name] : undefined; - } - - var func = new Function('functions', 'data', 'unknown', 'prop', js); - - return function(data, extraFunctions) { - // Modification to not require us to compile a new expression every time - if (extraFunctions) { - for (var name in extraFunctions) { - if (extraFunctions.hasOwnProperty(name)) { - functions[name] = extraFunctions[name]; - } - } - } - - return func(functions, data, unknown, prop); - }; -} - -function filtrexParser() { - - // Language parser powered by Jison , - // which is a pure JavaScript implementation of - // Bison . - - var Jison = require('jison'), - bnf = require('jison/bnf'); - - function code(args, skipParentheses) { - var argsJs = args.map(function(a) { - return typeof(a) == 'number' ? ('$' + a) : JSON.stringify(a); - }).join(','); - - return skipParentheses - ? '$$ = [' + argsJs + '];' - : '$$ = ["(", ' + argsJs + ', ")"];'; - } - - var grammar = { - // Lexical tokens - lex: { - rules: [ - ['\\*', 'return "*";'], - ['\\/', 'return "/";'], - ['-' , 'return "-";'], - ['\\+', 'return "+";'], - ['\\^', 'return "^";'], - ['\\%', 'return "%";'], - ['\\(', 'return "(";'], - ['\\)', 'return ")";'], - ['\\,', 'return ",";'], - ['==', 'return "==";'], - ['\\!=', 'return "!=";'], - ['\\~=', 'return "~=";'], - ['>=', 'return ">=";'], - ['<=', 'return "<=";'], - ['<', 'return "<";'], - ['>', 'return ">";'], - ['\\?', 'return "?";'], - ['\\:', 'return ":";'], - ['and[^\\w]', 'return "and";'], - ['or[^\\w]' , 'return "or";'], - ['not[^\\w]', 'return "not";'], - ['in[^\\w]', 'return "in";'], - - ['\\s+', ''], // skip whitespace - ['[0-9]+(?:\\.[0-9]+)?\\b', 'return "NUMBER";'], // 212.321 - - ['[a-zA-Z][\\.a-zA-Z0-9_]*', - `yytext = JSON.stringify(yytext); - return "SYMBOL";` - ], // some.Symbol22 - - [`'(?:[^\'])*'`, - `yytext = JSON.stringify( - yytext.substr(1, yyleng-2) - ); - return "SYMBOL";` - ], // 'some-symbol' - - ['"(?:[^"])*"', - `yytext = JSON.stringify( - yytext.substr(1, yyleng-2) - ); - return "STRING";` - ], // "foo" - - // End - ['$', 'return "EOF";'], - ] - }, - // Operator precedence - lowest precedence first. - // See http://www.gnu.org/software/bison/manual/html_node/Precedence.html - // for a good explanation of how it works in Bison (and hence, Jison). - // Different languages have different rules, but this seems a good starting - // point: http://en.wikipedia.org/wiki/Order_of_operations#Programming_languages - operators: [ - ['left', '?', ':'], - ['left', 'or'], - ['left', 'and'], - ['left', 'in'], - ['left', '==', '!=', '~='], - ['left', '<', '<=', '>', '>='], - ['left', '+', '-'], - ['left', '*', '/', '%'], - ['left', '^'], - ['left', 'not'], - ['left', 'UMINUS'], - ], - // Grammar - bnf: { - expressions: [ // Entry point - ['e EOF', 'return $1;'] - ], - e: [ - ['e + e' , code([1, '+', 3])], - ['e - e' , code([1, '-', 3])], - ['e * e' , code([1, '*', 3])], - ['e / e' , code([1, '/', 3])], - ['e % e' , code([1, '%', 3])], - ['e ^ e' , code(['Math.pow(', 1, ',', 3, ')'])], - ['- e' , code(['-', 2]), {prec: 'UMINUS'}], - ['e and e', code(['Number(', 1, '&&', 3, ')'])], - ['e or e' , code(['Number(', 1, '||', 3, ')'])], - ['not e' , code(['Number(!', 2, ')'])], - ['e == e' , code(['Number(', 1, '==', 3, ')'])], - ['e != e' , code(['Number(', 1, '!=', 3, ')'])], - ['e ~= e' , code(['RegExp(', 3, ').test(', 1, ')'])], - ['e < e' , code(['Number(', 1, '<' , 3, ')'])], - ['e <= e' , code(['Number(', 1, '<=', 3, ')'])], - ['e > e' , code(['Number(', 1, '> ', 3, ')'])], - ['e >= e' , code(['Number(', 1, '>=', 3, ')'])], - ['e ? e : e', code([1, '?', 3, ':', 5])], - ['( e )' , code([2])], - ['NUMBER' , code([1])], - ['STRING' , code([1])], - ['SYMBOL' , code(['prop(data, ', 1, ')'])], - ['SYMBOL ( )', code(['(functions.hasOwnProperty(', 1, ') ? functions[', 1, ']() : unknown(', 1, '))'])], - ['SYMBOL ( argsList )', code(['(functions.hasOwnProperty(', 1, ') ? functions[', 1, '](', 3, ') : unknown(', 1, '))'])], - ['e in ( inSet )', code(['(function(o) { return ', 4, '; })(', 1, ')'])], - ['e not in ( inSet )', code(['!(function(o) { return ', 5, '; })(', 1, ')'])], - ], - argsList: [ - ['e', code([1], true)], - ['argsList , e', code([1, ',', 3], true)], - ], - inSet: [ - ['e', code(['o ==', 1], true)], - ['inSet , e', code([1, '|| o ==', 3], true)], - ], - } - }; - return new Jison.Parser(grammar); -} - -// --------------------------------------------------- -// Jison will be appended after this point by Makefile -// --------------------------------------------------- -var require = (function() { -var require = (function () { - var modules = {}; - var factories = {}; - var r = function(id) { - if (!modules[id]) { - //console.log(id); - modules[id] = {}; - factories[id](r, modules[id], { id : id }); - } - return modules[id]; - }; - r.def = function(id, params) { - //console.log('def', id); - factories[id] = params.factory; - }; - return r; -})() -require.def("jison",{factory:function(require,exports,module){ -// Jison, an LR(0), SLR(1), LARL(1), LR(1) Parser Generator -// Zachary Carter -// MIT X Licensed - -var typal = require("jison/util/typal").typal, - Set = require("jison/util/set").Set, - RegExpLexer = require("jison/lexer").RegExpLexer; - -var Jison = exports.Jison = exports; - -// detect prints -Jison.print = function() { } -/* -if (typeof console !== 'undefined' && console.log) { - Jison.print = console.log; - Jison.print = function print () {}; -} else if (typeof puts !== 'undefined') { - Jison.print = function print () { puts([].join.call(arguments, ' ')); }; -} else if (typeof print !== 'undefined') { - Jison.print = print; -} else { - Jison.print = function print () {}; -} -*/ -Jison.Parser = (function () { - -// iterator utility -function each (obj, func) { - if (obj.forEach) { - obj.forEach(func); - } else { - var p; - for (p in obj) { - if (obj.hasOwnProperty(p)) { - func.call(obj, obj[p], p, obj); - } - } - } -} - -var Nonterminal = typal.construct({ - constructor: function Nonterminal (symbol) { - this.symbol = symbol; - this.productions = new Set(); - this.first = []; - this.follows = []; - this.nullable = false; - }, - toString: function Nonterminal_toString () { - var str = this.symbol+"\n"; - str += (this.nullable ? 'nullable' : 'not nullable'); - str += "\nFirsts: "+this.first.join(', '); - str += "\nFollows: "+this.first.join(', '); - str += "\nProductions:\n "+this.productions.join('\n '); - - return str; - } -}); - -var Production = typal.construct({ - constructor: function Production (symbol, handle, id) { - this.symbol = symbol; - this.handle = handle; - this.nullable = false; - this.id = id; - this.first = []; - this.precedence = 0; - }, - toString: function Production_toString () { - return this.symbol+" -> "+this.handle.join(' '); - } -}); - -var generator = typal.beget(); - -generator.constructor = function Jison_Generator (grammar, opt) { - if (typeof grammar === 'string') { - grammar = require("jison/bnf").parse(grammar); - } - - var options = typal.mix.call({}, grammar.options, opt); - this.terms = {}; - this.operators = {}; - this.productions = []; - this.conflicts = 0; - this.resolutions = []; - this.options = options; - this.yy = {}; // accessed as yy free variable in the parser/lexer actions - - // source included in semantic action execution scope - if (grammar.actionInclude) { - if (typeof grammar.actionInclude === 'function') { - grammar.actionInclude = String(grammar.actionInclude).replace(/^\s*function \(\) \{/, '').replace(/\}\s*$/, ''); - } - this.actionInclude = grammar.actionInclude; - } - this.moduleInclude = grammar.moduleInclude||''; - - this.DEBUG = options.debug || false; - if (this.DEBUG) this.mix(generatorDebug); // mixin debug methods - - this.processGrammar(grammar); - - if (grammar.lex) { - this.lexer = new RegExpLexer(grammar.lex, null, this.terminals_); - } -}; - -generator.processGrammar = function processGrammarDef (grammar) { - var bnf = grammar.bnf, - tokens = grammar.tokens, - nonterminals = this.nonterminals = {}, - productions = this.productions, - self = this; - - if (!grammar.bnf && grammar.ebnf) { - bnf = grammar.bnf = require("jison/ebnf").transform(grammar.ebnf); - } - - if (tokens) { - if (typeof tokens === 'string') { - tokens = tokens.trim().split(' '); - } else { - tokens = tokens.slice(0); - } - } - - var symbols = this.symbols = []; - - // calculate precedence of operators - var operators = this.operators = processOperators(grammar.operators); - - // build productions from cfg - this.buildProductions(grammar.bnf, productions, nonterminals, symbols, operators); - - if (tokens && this.terminals.length !== tokens.length) { - self.trace("Warning: declared tokens differ from tokens found in rules."); - self.trace(this.terminals); - self.trace(tokens); - } - - // augment the grammar - this.augmentGrammar(grammar); -}; - -generator.augmentGrammar = function augmentGrammar (grammar) { - // use specified start symbol, or default to first user defined production - this.startSymbol = grammar.start || grammar.startSymbol || this.productions[0].symbol; - if (!this.nonterminals[this.startSymbol]) { - throw new Error("Grammar error: startSymbol must be a non-terminal found in your grammar."); - } - this.EOF = "$end"; - - // augment the grammar - var acceptProduction = new Production('$accept', [this.startSymbol, '$end'], 0); - this.productions.unshift(acceptProduction); - - // prepend parser tokens - this.symbols.unshift("$accept",this.EOF); - this.symbols_.$accept = 0; - this.symbols_[this.EOF] = 1; - this.terminals.unshift(this.EOF); - - this.nonterminals.$accept = new Nonterminal("$accept"); - this.nonterminals.$accept.productions.push(acceptProduction); - - // add follow $ to start symbol - this.nonterminals[this.startSymbol].follows.push(this.EOF); -}; - -// set precedence and associativity of operators -function processOperators (ops) { - if (!ops) return {}; - var operators = {}; - for (var i=0,k,prec;prec=ops[i]; i++) { - for (k=1;k < prec.length;k++) { - operators[prec[k]] = {precedence: i+1, assoc: prec[0]}; - } - } - return operators; -} - - -generator.buildProductions = function buildProductions(bnf, productions, nonterminals, symbols, operators) { - var actions = [ - this.actionInclude || '', - 'var $0 = $$.length - 1;', - 'switch (yystate) {' - ]; - var prods, symbol; - var productions_ = [0]; - var symbolId = 1; - var symbols_ = {}; - - var her = false; // has error recovery - - function addSymbol (s) { - if (s && !symbols_[s]) { - symbols_[s] = ++symbolId; - symbols.push(s); - } - } - - // add error symbol; will be third symbol, or "2" ($accept, $end, error) - addSymbol("error"); - - for (symbol in bnf) { - if (!bnf.hasOwnProperty(symbol)) continue; - - addSymbol(symbol); - nonterminals[symbol] = new Nonterminal(symbol); - - if (typeof bnf[symbol] === 'string') { - prods = bnf[symbol].split(/\s*\|\s*/g); - } else { - prods = bnf[symbol].slice(0); - } - - prods.forEach(buildProduction); - } - - var sym, terms = [], terms_ = {}; - each(symbols_, function (id, sym) { - if (!nonterminals[sym]) { - terms.push(sym); - terms_[id] = sym; - } - }); - - this.hasErrorRecovery = her; - - this.terminals = terms; - this.terminals_ = terms_; - this.symbols_ = symbols_; - - this.productions_ = productions_; - actions.push('}'); - this.performAction = Function("yytext,yyleng,yylineno,yy,yystate,$$,_$", actions.join("\n")); - - function buildProduction (handle) { - var r, rhs, i; - if (handle.constructor === Array) { - rhs = (typeof handle[0] === 'string') ? - handle[0].trim().split(' ') : - handle[0].slice(0); - - for (i=0; i=0; i--) { - if (!(r.handle[i] in nonterminals) && r.handle[i] in operators) { - r.precedence = operators[r.handle[i]].precedence; - } - } - } - - productions.push(r); - productions_.push([symbols_[r.symbol], r.handle[0] === '' ? 0 : r.handle.length]); - nonterminals[symbol].productions.push(r); - } -}; - - - -generator.createParser = function createParser () { - throw new Error('Calling abstract method.'); -}; - -// noop. implemented in debug mixin -generator.trace = function trace () { }; - -generator.warn = function warn () { - var args = Array.prototype.slice.call(arguments,0); - console.warn('Jison Warning', args); -// Jison.print.call(null,args.join("")); -}; - -generator.error = function error (msg) { - throw new Error(msg); -}; - -// Generator debug mixin - -var generatorDebug = { - trace: function trace () { - Jison.print.apply(null, arguments); - }, - beforeprocessGrammar: function () { - this.trace("Processing grammar."); - }, - afteraugmentGrammar: function () { - var trace = this.trace; - each(this.symbols, function (sym, i) { - trace(sym+"("+i+")"); - }); - } -}; - - - -/* - * Mixin for common behaviors of lookahead parsers - * */ -var lookaheadMixin = {}; - -lookaheadMixin.computeLookaheads = function computeLookaheads () { - if (this.DEBUG) this.mix(lookaheadDebug); // mixin debug methods - - this.computeLookaheads = function () {}; - this.nullableSets(); - this.firstSets(); - this.followSets(); -}; - -// calculate follow sets typald on first and nullable -lookaheadMixin.followSets = function followSets () { - var productions = this.productions, - nonterminals = this.nonterminals, - self = this, - cont = true; - - // loop until no further changes have been made - while(cont) { - cont = false; - - productions.forEach(function Follow_prod_forEach (production, k) { - //self.trace(production.symbol,nonterminals[production.symbol].follows); - // q is used in Simple LALR algorithm determine follows in context - var q; - var ctx = !!self.go_; - - var set = [],oldcount; - for (var i=0,t;t=production.handle[i];++i) { - if (!nonterminals[t]) continue; - - // for Simple LALR algorithm, self.go_ checks if - if (ctx) - q = self.go_(production.symbol, production.handle.slice(0, i)); - var bool = !ctx || q === parseInt(self.nterms_[t], 10); - - if (i === production.handle.length+1 && bool) { - set = nonterminals[production.symbol].follows; - } else { - var part = production.handle.slice(i+1); - - set = self.first(part); - if (self.nullable(part) && bool) { - set.push.apply(set, nonterminals[production.symbol].follows); - } - } - oldcount = nonterminals[t].follows.length; - Set.union(nonterminals[t].follows, set); - if (oldcount !== nonterminals[t].follows.length) { - cont = true; - } - } - }); - } -}; - -// return the FIRST set of a symbol or series of symbols -lookaheadMixin.first = function first (symbol) { - // epsilon - if (symbol === '') { - return []; - // RHS - } else if (symbol instanceof Array) { - var firsts = []; - for (var i=0,t;t=symbol[i];++i) { - if (!this.nonterminals[t]) { - if (firsts.indexOf(t) === -1) - firsts.push(t); - } else { - Set.union(firsts, this.nonterminals[t].first); - } - if (!this.nullable(t)) - break; - } - return firsts; - // terminal - } else if (!this.nonterminals[symbol]) { - return [symbol]; - // nonterminal - } else { - return this.nonterminals[symbol].first; - } -}; - -// fixed-point calculation of FIRST sets -lookaheadMixin.firstSets = function firstSets () { - var productions = this.productions, - nonterminals = this.nonterminals, - self = this, - cont = true, - symbol,firsts; - - // loop until no further changes have been made - while(cont) { - cont = false; - - productions.forEach(function FirstSets_forEach (production, k) { - var firsts = self.first(production.handle); - if (firsts.length !== production.first.length) { - production.first = firsts; - cont=true; - } - }); - - for (symbol in nonterminals) { - firsts = []; - nonterminals[symbol].productions.forEach(function (production) { - Set.union(firsts, production.first); - }); - if (firsts.length !== nonterminals[symbol].first.length) { - nonterminals[symbol].first = firsts; - cont=true; - } - } - } -}; - -// fixed-point calculation of NULLABLE -lookaheadMixin.nullableSets = function nullableSets () { - var firsts = this.firsts = {}, - nonterminals = this.nonterminals, - self = this, - cont = true; - - // loop until no further changes have been made - while(cont) { - cont = false; - - // check if each production is nullable - this.productions.forEach(function (production, k) { - if (!production.nullable) { - for (var i=0,n=0,t;t=production.handle[i];++i) { - if (self.nullable(t)) n++; - } - if (n===i) { // production is nullable if all tokens are nullable - production.nullable = cont = true; - } - } - }); - - //check if each symbol is nullable - for (var symbol in nonterminals) { - if (!this.nullable(symbol)) { - for (var i=0,production;production=nonterminals[symbol].productions.item(i);i++) { - if (production.nullable) - nonterminals[symbol].nullable = cont = true; - } - } - } - } -}; - -// check if a token or series of tokens is nullable -lookaheadMixin.nullable = function nullable (symbol) { - // epsilon - if (symbol === '') { - return true; - // RHS - } else if (symbol instanceof Array) { - for (var i=0,t;t=symbol[i];++i) { - if (!this.nullable(t)) - return false; - } - return true; - // terminal - } else if (!this.nonterminals[symbol]) { - return false; - // nonterminal - } else { - return this.nonterminals[symbol].nullable; - } -}; - - -// lookahead debug mixin -var lookaheadDebug = { - beforenullableSets: function () { - this.trace("Computing Nullable sets."); - }, - beforefirstSets: function () { - this.trace("Computing First sets."); - }, - beforefollowSets: function () { - this.trace("Computing Follow sets."); - }, - afterfollowSets: function () { - var trace = this.trace; - each(this.nonterminals, function (nt, t) { - trace(nt, '\n'); - }); - } -}; - -/* - * Mixin for common LR parser behavior - * */ -var lrGeneratorMixin = {}; - -lrGeneratorMixin.buildTable = function buildTable () { - if (this.DEBUG) this.mix(lrGeneratorDebug); // mixin debug methods - - this.states = this.canonicalCollection(); - this.table = this.parseTable(this.states); - this.defaultActions = findDefaults(this.table); -}; - -lrGeneratorMixin.Item = typal.construct({ - constructor: function Item(production, dot, f, predecessor) { - this.production = production; - this.dotPosition = dot || 0; - this.follows = f || []; - this.predecessor = predecessor; - this.id = parseInt(production.id+'a'+this.dotPosition, 36); - this.markedSymbol = this.production.handle[this.dotPosition]; - }, - remainingHandle: function () { - return this.production.handle.slice(this.dotPosition+1); - }, - eq: function (e) { - return e.id === this.id; - }, - handleToString: function () { - var handle = this.production.handle.slice(0); - handle[this.dotPosition] = '.'+(handle[this.dotPosition]||''); - return handle.join(' '); - }, - toString: function () { - var temp = this.production.handle.slice(0); - temp[this.dotPosition] = '.'+(temp[this.dotPosition]||''); - return this.production.symbol+" -> "+temp.join(' ') + - (this.follows.length === 0 ? "" : " #lookaheads= "+this.follows.join(' ')); - } -}); - -lrGeneratorMixin.ItemSet = Set.prototype.construct({ - afterconstructor: function () { - this.reductions = []; - this.goes = {}; - this.edges = {}; - this.shifts = false; - this.inadequate = false; - this.hash_ = {}; - for (var i=this._items.length-1;i >=0;i--) { - this.hash_[this._items[i].id] = true; //i; - } - }, - concat: function concat (set) { - var a = set._items || set; - for (var i=a.length-1;i >=0;i--) { - this.hash_[a[i].id] = true; //i; - } - this._items.push.apply(this._items, a); - return this; - }, - push: function (item) { - this.hash_[item.id] = true; - return this._items.push(item); - }, - contains: function (item) { - return this.hash_[item.id]; - }, - valueOf: function toValue () { - var v = this._items.map(function (a) {return a.id;}).sort().join('|'); - this.valueOf = function toValue_inner() {return v;}; - return v; - } -}); - -lrGeneratorMixin.closureOperation = function closureOperation (itemSet /*, closureSet*/) { - var closureSet = new this.ItemSet(); - var self = this; - - var set = itemSet, - itemQueue, syms = {}; - - do { - itemQueue = new Set(); - closureSet.concat(set); - set.forEach(function CO_set_forEach (item) { - var symbol = item.markedSymbol; - - // if token is a non-terminal, recursively add closures - if (symbol && self.nonterminals[symbol]) { - if(!syms[symbol]) { - self.nonterminals[symbol].productions.forEach(function CO_nt_forEach (production) { - var newItem = new self.Item(production, 0); - if(!closureSet.contains(newItem)) - itemQueue.push(newItem); - }); - syms[symbol] = true; - } - } else if (!symbol) { - // reduction - closureSet.reductions.push(item); - closureSet.inadequate = closureSet.reductions.length > 1 || closureSet.shifts; - } else { - // shift - closureSet.shifts = true; - closureSet.inadequate = closureSet.reductions.length > 0; - } - }); - - set = itemQueue; - - } while (!itemQueue.isEmpty()); - - return closureSet; -}; - -lrGeneratorMixin.gotoOperation = function gotoOperation (itemSet, symbol) { - var gotoSet = new this.ItemSet(), - self = this; - - itemSet.forEach(function goto_forEach(item, n) { - if (item.markedSymbol === symbol) { - gotoSet.push(new self.Item(item.production, item.dotPosition+1, item.follows, n)); - } - }); - - return gotoSet.isEmpty() ? gotoSet : this.closureOperation(gotoSet); -}; - -/* Create unique set of item sets - * */ -lrGeneratorMixin.canonicalCollection = function canonicalCollection () { - var item1 = new this.Item(this.productions[0], 0, [this.EOF]); - var firstState = this.closureOperation(new this.ItemSet(item1)), - states = new Set(firstState), - marked = 0, - self = this, - itemSet; - - states.has = {}; - states.has[firstState] = 0; - - while (marked !== states.size()) { - itemSet = states.item(marked); marked++; - itemSet.forEach(function CC_itemSet_forEach (item) { - if (item.markedSymbol && item.markedSymbol !== self.EOF) - self.canonicalCollectionInsert(item.markedSymbol, itemSet, states, marked-1); - }); - } - - return states; -}; - -// Pushes a unique state into the que. Some parsing algorithms may perform additional operations -lrGeneratorMixin.canonicalCollectionInsert = function canonicalCollectionInsert (symbol, itemSet, states, stateNum) { - var g = this.gotoOperation(itemSet, symbol); - if (!g.predecessors) - g.predecessors = {}; - // add g to que if not empty or duplicate - if (!g.isEmpty()) { - var gv = g.valueOf(), - i = states.has[gv]; - if (i === -1 || typeof i === 'undefined') { - states.has[gv] = states.size(); - itemSet.edges[symbol] = states.size(); // store goto transition for table - states.push(g); - g.predecessors[symbol] = [stateNum]; - } else { - itemSet.edges[symbol] = i; // store goto transition for table - states.item(i).predecessors[symbol].push(stateNum); - } - } -}; - -var NONASSOC = 0; -lrGeneratorMixin.parseTable = function parseTable (itemSets) { - var NONASSOC = 0; - var states = [], - nonterminals = this.nonterminals, - operators = this.operators, - conflictedStates = {}, // array of [state, token] tuples - self = this, - s = 1, // shift - r = 2, // reduce - a = 3; // accept - - // for each item set - itemSets.forEach(function (itemSet, k) { - var state = states[k] = {}; - var action, stackSymbol; - - // set shift and goto actions - for (stackSymbol in itemSet.edges) { - itemSet.forEach(function (item, j) { - // find shift and goto actions - if (item.markedSymbol == stackSymbol) { - var gotoState = itemSet.edges[stackSymbol]; - if (nonterminals[stackSymbol]) { - // store state to go to after a reduce - //self.trace(k, stackSymbol, 'g'+gotoState); - state[self.symbols_[stackSymbol]] = gotoState; - } else { - //self.trace(k, stackSymbol, 's'+gotoState); - state[self.symbols_[stackSymbol]] = [s,gotoState]; - } - } - }); - } - - // set accept action - itemSet.forEach(function (item, j) { - if (item.markedSymbol == self.EOF) { - // accept - state[self.symbols_[self.EOF]] = [a]; - //self.trace(k, self.EOF, state[self.EOF]); - } - }); - - var allterms = self.lookAheads ? false : self.terminals; - - // set reductions and resolve potential conflicts - itemSet.reductions.forEach(function (item, j) { - // if parser uses lookahead, only enumerate those terminals - var terminals = allterms || self.lookAheads(itemSet, item); - - terminals.forEach(function (stackSymbol) { - action = state[self.symbols_[stackSymbol]]; - var op = operators[stackSymbol]; - - // Reading a terminal and current position is at the end of a production, try to reduce - if (action || action && action.length) { - var sol = resolveConflict(item.production, op, [r,item.production.id], action[0] instanceof Array ? action[0] : action); - self.resolutions.push([k,stackSymbol,sol]); - if (sol.bydefault) { - self.conflicts++; - if (!self.DEBUG) { - self.warn('Conflict in grammar: multiple actions possible when lookahead token is ',stackSymbol,' in state ',k, "\n- ", printAction(sol.r, self), "\n- ", printAction(sol.s, self)); - conflictedStates[k] = true; - } - if (self.options.noDefaultResolve) { - if (!(action[0] instanceof Array)) - action = [action]; - action.push(sol.r); - } - } else { - action = sol.action; - } - } else { - action = [r,item.production.id]; - } - if (action && action.length) { - state[self.symbols_[stackSymbol]] = action; - } else if (action === NONASSOC) { - state[self.symbols_[stackSymbol]] = undefined; - } - }); - }); - - }); - - if (!self.DEBUG && self.conflicts > 0) { - self.warn("\nStates with conflicts:"); - each(conflictedStates, function (val, state) { - self.warn('State '+state); - self.warn(' ',itemSets.item(state).join("\n ")); - }); - } - - return states; -}; - -// find states with only one action, a reduction -function findDefaults (states) { - var defaults = {}; - states.forEach(function (state, k) { - var i = 0; - for (var act in state) { - if ({}.hasOwnProperty.call(state, act)) i++; - } - - if (i === 1 && state[act][0] === 2) { - // only one action in state and it's a reduction - defaults[k] = state[act]; - } - }); - - return defaults; -} - -// resolves shift-reduce and reduce-reduce conflicts -function resolveConflict (production, op, reduce, shift) { - var sln = {production: production, operator: op, r: reduce, s: shift}, - s = 1, // shift - r = 2, // reduce - a = 3; // accept - - if (shift[0] === r) { - sln.msg = "Resolve R/R conflict (use first production declared in grammar.)"; - sln.action = shift[1] < reduce[1] ? shift : reduce; - if (shift[1] !== reduce[1]) sln.bydefault = true; - return sln; - } - - if (production.precedence === 0 || !op) { - sln.msg = "Resolve S/R conflict (shift by default.)"; - sln.bydefault = true; - sln.action = shift; - } else if (production.precedence < op.precedence ) { - sln.msg = "Resolve S/R conflict (shift for higher precedent operator.)"; - sln.action = shift; - } else if (production.precedence === op.precedence) { - if (op.assoc === "right" ) { - sln.msg = "Resolve S/R conflict (shift for right associative operator.)"; - sln.action = shift; - } else if (op.assoc === "left" ) { - sln.msg = "Resolve S/R conflict (reduce for left associative operator.)"; - sln.action = reduce; - } else if (op.assoc === "nonassoc" ) { - sln.msg = "Resolve S/R conflict (no action for non-associative operator.)"; - sln.action = NONASSOC; - } - } else { - sln.msg = "Resolve conflict (reduce for higher precedent production.)"; - sln.action = reduce; - } - - return sln; -} - -lrGeneratorMixin.generate = function parser_generate (opt) { - opt = typal.mix.call({}, this.options, opt); - var code = ""; - - // check for illegal identifier - if (!opt.moduleName || !opt.moduleName.match(/^[A-Za-z_$][A-Za-z0-9_$]*$/)) { - opt.moduleName = "parser"; - } - switch (opt.moduleType) { - case "js": - code = this.generateModule(opt); - break; - case "amd": - code = this.generateAMDModule(opt); - break; - default: - code = this.generateCommonJSModule(opt); - } - - return code; -}; - -lrGeneratorMixin.generateAMDModule = function generateAMDModule(opt){ - opt = typal.mix.call({}, this.options, opt); - var out = 'define([], function(){' - + '\nvar parser = '+ this.generateModule_(opt) - + (this.lexer && this.lexer.generateModule ? - '\n' + this.lexer.generateModule() + - '\nparser.lexer = lexer;' : '') - + '\nreturn parser;' - + '\n});' - return out; -}; - -lrGeneratorMixin.generateCommonJSModule = function generateCommonJSModule (opt) { - opt = typal.mix.call({}, this.options, opt); - var moduleName = opt.moduleName || "parser"; - var out = this.generateModule(opt) - + "\nif (typeof require !== 'undefined' && typeof exports !== 'undefined') {" - + "\nexports.parser = "+moduleName+";" - + "\nexports.Parser = "+moduleName+".Parser;" - + "\nexports.parse = function () { return "+moduleName+".parse.apply("+moduleName+", arguments); }" - + "\nexports.main = "+ String(opt.moduleMain || commonjsMain) - + "\nif (typeof module !== 'undefined' && require.main === module) {\n" - + " exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require(\"system\").args);\n}" - + "\n}" - - return out; -}; - -lrGeneratorMixin.generateModule = function generateModule (opt) { - opt = typal.mix.call({}, this.options, opt); - var moduleName = opt.moduleName || "parser"; - var out = "/* Jison generated parser */\n"; - out += (moduleName.match(/\./) ? moduleName : "var "+moduleName)+" = (function(){"; - out += "\nvar parser = "+this.generateModule_(); - out += "\n"+this.moduleInclude; - if (this.lexer && this.lexer.generateModule) { - out += this.lexer.generateModule(); - out += "\nparser.lexer = lexer;"; - } - out += "\nfunction Parser () { this.yy = {}; }" - + "Parser.prototype = parser;" - + "parser.Parser = Parser;" - + "\nreturn new Parser;\n})();"; - - return out; -}; - -// returns parse function without error recovery code -function removeErrorRecovery (fn) { - var parseFn = String(fn); - try { - var JSONSelect = require("JSONSelect"); - var Reflect = require("reflect"); - var ast = Reflect.parse(parseFn); - - var labeled = JSONSelect.match(':has(:root > .label > .name:val("_handle_error"))', ast); - labeled[0].body.consequent.body = [labeled[0].body.consequent.body[0], labeled[0].body.consequent.body[1]]; - - return Reflect.stringify(ast).replace(/_handle_error:\s?/,"").replace(/\\\\n/g,"\\n"); - } catch (e) { - return parseFn; - } -} - -lrGeneratorMixin.generateModule_ = function generateModule_ () { - var parseFn = (this.hasErrorRecovery ? String : removeErrorRecovery)(parser.parse); - - var out = "{"; - out += [ - "trace: " + String(this.trace || parser.trace), - "yy: {}", - "symbols_: " + JSON.stringify(this.symbols_), - "terminals_: " + JSON.stringify(this.terminals_).replace(/"([0-9]+)":/g,"$1:"), - "productions_: " + JSON.stringify(this.productions_), - "performAction: " + String(this.performAction), - "table: " + JSON.stringify(this.table).replace(/"([0-9]+)":/g,"$1:"), - "defaultActions: " + JSON.stringify(this.defaultActions).replace(/"([0-9]+)":/g,"$1:"), - "parseError: " + String(this.parseError || (this.hasErrorRecovery ? traceParseError : parser.parseError)), - "parse: " + parseFn - ].join(",\n"); - out += "};"; - - return out; -}; - -// default main method for generated commonjs modules -function commonjsMain (args) { - if (!args[1]) - throw new Error('Usage: '+args[0]+' FILE'); - var source, cwd; - if (typeof process !== 'undefined') { - source = require("fs").readFileSync(require("path").resolve(args[1]), "utf8"); - } else { - source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"}); - } - return exports.parser.parse(source); -} - -// debug mixin for LR parser generators - -function printAction (a, gen) { - var s = a[0] == 1 ? 'shift token (then go to state '+a[1]+')' : - a[0] == 2 ? 'reduce by rule: '+gen.productions[a[1]] : - 'accept' ; - - return s; -} - -var lrGeneratorDebug = { - beforeparseTable: function () { - this.trace("Building parse table."); - }, - afterparseTable: function () { - var self = this; - if (this.conflicts > 0) { - this.resolutions.forEach(function (r, i) { - if (r[2].bydefault) { - self.warn('Conflict at state: ',r[0], ', token: ',r[1], "\n ", printAction(r[2].r, self), "\n ", printAction(r[2].s, self)); - } - }); - this.trace("\n"+this.conflicts+" Conflict(s) found in grammar."); - } - this.trace("Done."); - }, - aftercanonicalCollection: function (states) { - var trace = this.trace; - trace("\nItem sets\n------"); - - states.forEach(function (state, i) { - trace("\nitem set",i,"\n"+state.join("\n"), '\ntransitions -> ', JSON.stringify(state.edges)); - }); - } -}; - -var parser = typal.beget(); - -lrGeneratorMixin.createParser = function createParser () { - var p = parser.beget(); - p.yy = {}; - - p.init({ - table: this.table, - defaultActions: this.defaultActions, - productions_: this.productions_, - symbols_: this.symbols_, - terminals_: this.terminals_, - performAction: this.performAction - }); - - // don't throw if grammar recovers from errors - if (this.hasErrorRecovery) { - p.parseError = traceParseError; - p.recover = true; - } - - // for debugging - p.productions = this.productions; - - // backwards compatability - p.generate = this.generate; - p.lexer = this.lexer; - p.generateModule = this.generateModule; - p.generateCommonJSModule = this.generateCommonJSModule; - p.generateModule_ = this.generateModule_; - - var gen = this; - - p.Parser = function () { - return gen.createParser(); - }; - - return p; -}; - -parser.trace = generator.trace; -parser.warn = generator.warn; -parser.error = generator.error; - -function traceParseError (err, hash) { - this.trace(err); -} - -parser.parseError = lrGeneratorMixin.parseError = function parseError (str, hash) { - throw new Error(str); -}; - -parser.parse = function parse (input) { - var self = this, - stack = [0], - vstack = [null], // semantic value stack - lstack = [], // location stack - table = this.table, - yytext = '', - yylineno = 0, - yyleng = 0, - recovering = 0, - TERROR = 2, - EOF = 1; - - //this.reductionCount = this.shiftCount = 0; - - this.lexer.setInput(input); - this.lexer.yy = this.yy; - this.yy.lexer = this.lexer; - this.yy.parser = this; - if (typeof this.lexer.yylloc == 'undefined') - this.lexer.yylloc = {}; - var yyloc = this.lexer.yylloc; - lstack.push(yyloc); - - var ranges = this.lexer.options && this.lexer.options.ranges; - - if (typeof this.yy.parseError === 'function') - this.parseError = this.yy.parseError; - - function popStack (n) { - stack.length = stack.length - 2*n; - vstack.length = vstack.length - n; - lstack.length = lstack.length - n; - } - - function lex() { - var token; - token = self.lexer.lex() || 1; // $end = 1 - // if token isn't its numeric value, convert - if (typeof token !== 'number') { - token = self.symbols_[token] || token; - } - return token; - } - - var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected; - while (true) { - // retreive state number from top of stack - state = stack[stack.length-1]; - - // use default actions if available - if (this.defaultActions[state]) { - action = this.defaultActions[state]; - } else { - if (symbol === null || typeof symbol == 'undefined') { - symbol = lex(); - } - // read action for current state and first input - action = table[state] && table[state][symbol]; - } - - // handle parse error - _handle_error: - if (typeof action === 'undefined' || !action.length || !action[0]) { - - var errStr = ''; - if (!recovering) { - // Report error - expected = []; - for (p in table[state]) if (this.terminals_[p] && p > 2) { - expected.push("'"+this.terminals_[p]+"'"); - } - if (this.lexer.showPosition) { - errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + (this.terminals_[symbol] || symbol)+ "'"; - } else { - errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " + - (symbol == 1 /*EOF*/ ? "end of input" : - ("'"+(this.terminals_[symbol] || symbol)+"'")); - } - this.parseError(errStr, - {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - - // just recovered from another error - if (recovering == 3) { - if (symbol == EOF) { - throw new Error(errStr || 'Parsing halted.'); - } - - // discard current lookahead and grab another - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - symbol = lex(); - } - - // try to recover from error - while (1) { - // check for error recovery rule in this state - if ((TERROR.toString()) in table[state]) { - break; - } - if (state === 0) { - throw new Error(errStr || 'Parsing halted.'); - } - popStack(1); - state = stack[stack.length-1]; - } - - preErrorSymbol = symbol == 2 ? null : symbol; // save the lookahead token - symbol = TERROR; // insert generic error symbol as new lookahead - state = stack[stack.length-1]; - action = table[state] && table[state][TERROR]; - recovering = 3; // allow 3 real symbols to be shifted before reporting a new error - } - - // this shouldn't happen, unless resolve defaults are off - if (action[0] instanceof Array && action.length > 1) { - throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol); - } - - switch (action[0]) { - - case 1: // shift - //this.shiftCount++; - - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); // push state - symbol = null; - if (!preErrorSymbol) { // normal execution/no error - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { // error just occurred, resume old lookahead f/ before error - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - - case 2: // reduce - //this.reductionCount++; - - len = this.productions_[action[1]][1]; - - // perform semantic action - yyval.$ = vstack[vstack.length-len]; // default to $$ = $1 - // default location, uses first token for firsts, last for lasts - yyval._$ = { - first_line: lstack[lstack.length-(len||1)].first_line, - last_line: lstack[lstack.length-1].last_line, - first_column: lstack[lstack.length-(len||1)].first_column, - last_column: lstack[lstack.length-1].last_column - }; - if (ranges) { - yyval._$.range = [lstack[lstack.length-(len||1)].range[0], lstack[lstack.length-1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - - if (typeof r !== 'undefined') { - return r; - } - - // pop off stack - if (len) { - stack = stack.slice(0,-1*len*2); - vstack = vstack.slice(0, -1*len); - lstack = lstack.slice(0, -1*len); - } - - stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce) - vstack.push(yyval.$); - lstack.push(yyval._$); - // goto new state = table[STATE][NONTERMINAL] - newState = table[stack[stack.length-2]][stack[stack.length-1]]; - stack.push(newState); - break; - - case 3: // accept - return true; - } - - } - - return true; -}; - -parser.init = function parser_init (dict) { - this.table = dict.table; - this.defaultActions = dict.defaultActions; - this.performAction = dict.performAction; - this.productions_ = dict.productions_; - this.symbols_ = dict.symbols_; - this.terminals_ = dict.terminals_; -}; - -/* - * LR(0) Parser - * */ - -var lr0 = generator.beget(lookaheadMixin, lrGeneratorMixin, { - type: "LR(0)", - afterconstructor: function lr0_afterconstructor () { - this.buildTable(); - } -}); - -var LR0Generator = exports.LR0Generator = lr0.construct(); - -/* - * Simple LALR(1) - * */ - -var lalr = generator.beget(lookaheadMixin, lrGeneratorMixin, { - type: "LALR(1)", - - afterconstructor: function (grammar, options) { - if (this.DEBUG) this.mix(lrGeneratorDebug, lalrGeneratorDebug); // mixin debug methods - - options = options || {}; - this.states = this.canonicalCollection(); - this.terms_ = {}; - - var newg = this.newg = typal.beget(lookaheadMixin,{ - oldg: this, - trace: this.trace, - nterms_: {}, - DEBUG: false, - go_: function (r, B) { - r = r.split(":")[0]; // grab state # - B = B.map(function (b) { return b.slice(b.indexOf(":")+1); }); - return this.oldg.go(r, B); - } - }); - newg.nonterminals = {}; - newg.productions = []; - - this.inadequateStates = []; - - // if true, only lookaheads in inadequate states are computed (faster, larger table) - // if false, lookaheads for all reductions will be computed (slower, smaller table) - this.onDemandLookahead = options.onDemandLookahead || false; - - this.buildNewGrammar(); - newg.computeLookaheads(); - this.unionLookaheads(); - - this.table = this.parseTable(this.states); - this.defaultActions = findDefaults(this.table); - }, - - lookAheads: function LALR_lookaheads (state, item) { - return (!!this.onDemandLookahead && !state.inadequate) ? this.terminals : item.follows; - }, - go: function LALR_go (p, w) { - var q = parseInt(p, 10); - for (var i=0;i - -var RegExpLexer = (function () { - -// expand macros and convert matchers to RegExp's -function prepareRules(rules, macros, actions, tokens, startConditions, caseless) { - var m,i,k,action,conditions, - newRules = []; - - if (macros) { - macros = prepareMacros(macros); - } - - function tokenNumberReplacement (str, token) { - return "return "+(tokens[token] || "'"+token+"'"); - } - - actions.push('switch($avoiding_name_collisions) {'); - - for (i=0;i < rules.length; i++) { - if (Object.prototype.toString.apply(rules[i][0]) !== '[object Array]') { - // implicit add to all inclusive start conditions - for (k in startConditions) { - if (startConditions[k].inclusive) { - startConditions[k].rules.push(i); - } - } - } else if (rules[i][0][0] === '*') { - // Add to ALL start conditions - for (k in startConditions) { - startConditions[k].rules.push(i); - } - rules[i].shift(); - } else { - // Add to explicit start conditions - conditions = rules[i].shift(); - for (k=0;k 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, - // displays upcoming input, i.e. for error messages - upcomingInput: function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, - // displays upcoming input, i.e. for error messages - showPosition: function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, - - // return next match in input - next: function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, - - // return next match that has a token - lex: function lex () { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, - begin: function begin (condition) { - this.conditionStack.push(condition); - }, - popState: function popState () { - return this.conditionStack.pop(); - }, - _currentRules: function _currentRules () { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, - topState: function () { - return this.conditionStack[this.conditionStack.length-2]; - }, - pushState: function begin (condition) { - this.begin(condition); - }, - - generate: function generate(opt) { - var code = ""; - if (opt.commonjs) - code = this.generateCommonJSModule(opt); - else - code = this.generateModule(opt); - - return code; - }, - generateModule: function generateModule(opt) { - opt = opt || {}; - var out = "/* Jison generated lexer */", - moduleName = opt.moduleName || "lexer"; - out += "\nvar "+moduleName+" = (function(){\nvar lexer = ({"; - var p = []; - for (var k in RegExpLexer.prototype) - if (RegExpLexer.prototype.hasOwnProperty(k) && k.indexOf("generate") === -1) - p.push(k + ":" + (RegExpLexer.prototype[k].toString() || '""')); - out += p.join(",\n"); - out += "})"; - if (this.options) { - out += ";\nlexer.options = "+JSON.stringify(this.options); - } - out += ";\nlexer.performAction = "+String(this.performAction); - out += ";\nlexer.rules = [" + this.rules + "]"; - out += ";\nlexer.conditions = " + JSON.stringify(this.conditions); - if (this.moduleInclude) out += ";\n"+this.moduleInclude; - out += ";\nreturn lexer;})()"; - return out; - }, - generateCommonJSModule: function generateCommonJSModule(opt) { - opt = opt || {}; - var out = "/* Jison generated lexer as commonjs module */", - moduleName = opt.moduleName || "lexer"; - out += this.generateModule(opt); - out += "\nexports.lexer = "+moduleName; - out += ";\nexports.lex = function () { return "+moduleName+".lex.apply(lexer, arguments); };"; - return out; - } -}; - -return RegExpLexer; - -})(); - -if (typeof exports !== 'undefined') - exports.RegExpLexer = RegExpLexer; - - -//*/ -},requires:["jison/jisonlex"]}); - -require.def("jison/bnf",{factory:function(require,exports,module){ -var bnf = require("jison/util/bnf-parser").parser, - jisonlex = require("jison/jisonlex"); - -exports.parse = function parse () { return bnf.parse.apply(bnf, arguments); }; - -// adds a declaration to the grammar -bnf.yy.addDeclaration = function (grammar, decl) { - if (decl.start) { - grammar.start = decl.start; - } - else if (decl.lex) { - grammar.lex = parseLex(decl.lex); - } - else if (decl.operator) { - if (!grammar.operators) { - grammar.operators = []; - } - grammar.operators.push(decl.operator); - } - else if (decl.include) { - if (!grammar.moduleInclude) - grammar.moduleInclude = ''; - grammar.moduleInclude += decl.include; - } - -}; - -// helps tokenize comments -bnf.yy.lexComment = function (lexer) { - var ch = lexer.input(); - if (ch === '/') { - lexer.yytext = lexer.yytext.replace(/\*(.|\s)\/\*/, '*$1'); - return; - } else { - lexer.unput('/*'); - lexer.more(); - } -}; - -// parse an embedded lex section -var parseLex = function (text) { - return jisonlex.parse(text.replace(/(?:^%lex)|(?:\/lex$)/g, '')); -}; - - -//*/ -},requires:["jison/util/bnf-parser","jison/jisonlex"]}); - -require.def("jison/jisonlex",{factory:function(require,exports,module){ -var jisonlex = require("jison/util/lex-parser").parser; - -var parse_ = jisonlex.parse; -jisonlex.parse = exports.parse = function parse () { - jisonlex.yy.ruleSection = false; - return parse_.apply(jisonlex, arguments); -}; - -function encodeRE (s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1').replace(/\\\\u([a-fA-F0-9]{4})/g,'\\u$1'); } - -jisonlex.yy = { - prepareString: function (s) { - // unescape slashes - s = s.replace(/\\\\/g, "\\"); - s = encodeRE(s); - return s; - } -}; - -//*/ -},requires:["jison/util/lex-parser"]}); - -require.def("jison/util/set",{factory:function(require,exports,module){ -// Set class to wrap arrays - -var typal = require("jison/util/typal").typal; - -var setMixin = { - constructor: function Set_constructor (set, raw) { - this._items = []; - if (set && set.constructor === Array) - this._items = raw ? set: set.slice(0); - else if(arguments.length) - this._items = [].slice.call(arguments,0); - }, - concat: function concat (setB) { - this._items.push.apply(this._items, setB._items || setB); - return this; - }, - eq: function eq (set) { - return this._items.length === set._items.length && this.subset(set); - }, - indexOf: function indexOf (item) { - if(item && item.eq) { - for(var k=0; k=0;--k) { - ar[a[k]] = true; - } - for (var i=b.length-1;i >= 0;--i) { - if (!ar[b[i]]) { - a.push(b[i]); - } - } - return a; - } -}); - -if (typeof exports !== 'undefined') - exports.Set = Set; - - -//*/ -},requires:["jison/util/typal"]}); - -require.def("jison/util/typal",{factory:function(require,exports,module){ -/* - * Introduces a typal object to make classical/prototypal patterns easier - * Plus some AOP sugar - * - * By Zachary Carter - * MIT Licensed - * */ - -var typal = (function () { - -var create = Object.create || function (o) { function F(){} F.prototype = o; return new F(); }; -var position = /^(before|after)/; - -// basic method layering -// always returns original method's return value -function layerMethod(k, fun) { - var pos = k.match(position)[0], - key = k.replace(position, ''), - prop = this[key]; - - if (pos === 'after') { - this[key] = function () { - var ret = prop.apply(this, arguments); - var args = [].slice.call(arguments); - args.splice(0, 0, ret); - fun.apply(this, args); - return ret; - }; - } else if (pos === 'before') { - this[key] = function () { - fun.apply(this, arguments); - var ret = prop.apply(this, arguments); - return ret; - }; - } -} - -// mixes each argument's own properties into calling object, -// overwriting them or layering them. i.e. an object method 'meth' is -// layered by mixin methods 'beforemeth' or 'aftermeth' -function typal_mix() { - var self = this; - for(var i=0,o,k; i 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; - } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); - } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - if (ranges) { - yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; -} -}; - -var ebnf = false; - - -/* Jison generated lexer */ -var lexer = (function(){ -var lexer = ({EOF:1, -parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, -setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - if (this.options.ranges) this.yylloc.range = [0,0]; - this.offset = 0; - return this; - }, -input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) this.yylloc.range[1]++; - - this._input = this._input.slice(1); - return ch; - }, -unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length-len-1); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length-1); - this.matched = this.matched.substr(0, this.matched.length-1); - - if (lines.length-1) this.yylineno -= lines.length-1; - var r = this.yylloc.range; - - this.yylloc = {first_line: this.yylloc.first_line, - last_line: this.yylineno+1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: - this.yylloc.first_column - len - }; - - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - return this; - }, -more:function () { - this._more = true; - return this; - }, -less:function (n) { - this.unput(this.match.slice(n)); - }, -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, -lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, -begin:function begin(condition) { - this.conditionStack.push(condition); - }, -popState:function popState() { - return this.conditionStack.pop(); - }, -_currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, -topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, -pushState:function begin(condition) { - this.begin(condition); - }}); -lexer.options = {}; -lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - -var YYSTATE=YY_START -switch($avoiding_name_collisions) { -case 0:this.begin('code');return 5; -break; -case 1:return 38 -break; -case 2:return 39 -break; -case 3:return 40 -break; -case 4:return 41 -break; -case 5:return 42 -break; -case 6:/* skip whitespace */ -break; -case 7:/* skip comment */ -break; -case 8:return yy.lexComment(this); -break; -case 9:return 36; -break; -case 10:yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 37; -break; -case 11:yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 37; -break; -case 12:return 24; -break; -case 13:return 26; -break; -case 14:return 27; -break; -case 15:this.begin(ebnf ? 'ebnf' : 'bnf');return 5; -break; -case 16:if (!yy.options) yy.options = {}; ebnf = yy.options.ebnf = true; -break; -case 17:return 43; -break; -case 18:return 11; -break; -case 19:return 18; -break; -case 20:return 19; -break; -case 21:return 20; -break; -case 22:return 13; -break; -case 23:/* ignore unrecognized decl */ -break; -case 24:/* ignore type */ -break; -case 25:yy_.yytext = yy_.yytext.substr(2, yy_.yyleng-4); return 15; -break; -case 26:yy_.yytext = yy_.yytext.substr(2, yy_.yytext.length-4);return 15; -break; -case 27:yy.depth=0; this.begin('action'); return 44; -break; -case 28:yy_.yytext = yy_.yytext.substr(2, yy_.yyleng-2); return 47; -break; -case 29:/* ignore bad characters */ -break; -case 30:return 8; -break; -case 31:return 48; -break; -case 32:yy.depth++; return 44; -break; -case 33:yy.depth==0? this.begin(ebnf ? 'ebnf' : 'bnf') : yy.depth--; return 46; -break; -case 34:return 9; -break; -} -}; -lexer.rules = [/^(?:%%)/,/^(?:\()/,/^(?:\))/,/^(?:\*)/,/^(?:\?)/,/^(?:\+)/,/^(?:\s+)/,/^(?:\/\/.*)/,/^(?:\/\*[^*]*\*)/,/^(?:[a-zA-Z][a-zA-Z0-9_-]*)/,/^(?:"[^"]+")/,/^(?:'[^']+')/,/^(?::)/,/^(?:;)/,/^(?:\|)/,/^(?:%%)/,/^(?:%ebnf\b)/,/^(?:%prec\b)/,/^(?:%start\b)/,/^(?:%left\b)/,/^(?:%right\b)/,/^(?:%nonassoc\b)/,/^(?:%lex[\w\W]*?\/lex\b)/,/^(?:%[a-zA-Z]+[^\n]*)/,/^(?:<[a-zA-Z]*>)/,/^(?:\{\{[\w\W]*?\}\})/,/^(?:%\{(.|\n)*?%\})/,/^(?:\{)/,/^(?:->.*)/,/^(?:.)/,/^(?:$)/,/^(?:[^{}]+)/,/^(?:\{)/,/^(?:\})/,/^(?:(.|\n)+)/]; -lexer.conditions = {"bnf":{"rules":[0,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":true},"ebnf":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":true},"action":{"rules":[30,31,32,33],"inclusive":false},"code":{"rules":[30,34],"inclusive":false},"INITIAL":{"rules":[6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":true}}; - -; -return lexer;})() -parser.lexer = lexer; -function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; -return new Parser; -})(); -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { -exports.parser = bnf; -exports.Parser = bnf.Parser; -exports.parse = function () { return bnf.parse.apply(bnf, arguments); } -exports.main = function commonjsMain(args) { - if (!args[1]) - throw new Error('Usage: '+args[0]+' FILE'); - var source, cwd; - if (typeof process !== 'undefined') { - source = require("fs").readFileSync(require("path").resolve(args[1]), "utf8"); - } else { - source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"}); - } - return exports.parser.parse(source); -} -if (typeof module !== 'undefined' && require.main === module) { - exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); -} -} -//*/ -},requires:["fs","path","file","file","system"]}); - -require.def("jison/util/lex-parser",{factory:function(require,exports,module){ -/* Jison generated parser */ -var jisonlex = (function(){ -var parser = {trace: function trace() { }, -yy: {}, -symbols_: {"error":2,"lex":3,"definitions":4,"%%":5,"rules":6,"epilogue":7,"EOF":8,"CODE":9,"definition":10,"ACTION":11,"NAME":12,"regex":13,"START_INC":14,"names_inclusive":15,"START_EXC":16,"names_exclusive":17,"START_COND":18,"rule":19,"start_conditions":20,"action":21,"{":22,"action_body":23,"}":24,"ACTION_BODY":25,"<":26,"name_list":27,">":28,"*":29,",":30,"regex_list":31,"|":32,"regex_concat":33,"regex_base":34,"(":35,")":36,"SPECIAL_GROUP":37,"+":38,"?":39,"/":40,"/!":41,"name_expansion":42,"range_regex":43,"any_group_regex":44,".":45,"^":46,"$":47,"string":48,"escape_char":49,"NAME_BRACE":50,"ANY_GROUP_REGEX":51,"ESCAPE_CHAR":52,"RANGE_REGEX":53,"STRING_LIT":54,"CHARACTER_LIT":55,"$accept":0,"$end":1}, -terminals_: {2:"error",5:"%%",8:"EOF",9:"CODE",11:"ACTION",12:"NAME",14:"START_INC",16:"START_EXC",18:"START_COND",22:"{",24:"}",25:"ACTION_BODY",26:"<",28:">",29:"*",30:",",32:"|",35:"(",36:")",37:"SPECIAL_GROUP",38:"+",39:"?",40:"/",41:"/!",45:".",46:"^",47:"$",50:"NAME_BRACE",51:"ANY_GROUP_REGEX",52:"ESCAPE_CHAR",53:"RANGE_REGEX",54:"STRING_LIT",55:"CHARACTER_LIT"}, -productions_: [0,[3,4],[7,1],[7,2],[7,3],[4,2],[4,2],[4,0],[10,2],[10,2],[10,2],[15,1],[15,2],[17,1],[17,2],[6,2],[6,1],[19,3],[21,3],[21,1],[23,0],[23,1],[23,5],[23,4],[20,3],[20,3],[20,0],[27,1],[27,3],[13,1],[31,3],[31,2],[31,1],[31,0],[33,2],[33,1],[34,3],[34,3],[34,2],[34,2],[34,2],[34,2],[34,2],[34,1],[34,2],[34,1],[34,1],[34,1],[34,1],[34,1],[34,1],[42,1],[44,1],[49,1],[43,1],[48,1],[48,1]], -performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) { - -var $0 = $$.length - 1; -switch (yystate) { -case 1: this.$ = {rules: $$[$0-1]}; - if ($$[$0-3][0]) this.$.macros = $$[$0-3][0]; - if ($$[$0-3][1]) this.$.startConditions = $$[$0-3][1]; - if ($$[$0]) this.$.moduleInclude = $$[$0]; - if (yy.options) this.$.options = yy.options; - if (yy.actionInclude) this.$.actionInclude = yy.actionInclude; - delete yy.options; - delete yy.actionInclude; - return this.$; -break; -case 2: this.$ = null; -break; -case 3: this.$ = null; -break; -case 4: this.$ = $$[$0-1]; -break; -case 5: - this.$ = $$[$0]; - if ('length' in $$[$0-1]) { - this.$[0] = this.$[0] || {}; - this.$[0][$$[$0-1][0]] = $$[$0-1][1]; - } else { - this.$[1] = this.$[1] || {}; - for (var name in $$[$0-1]) { - this.$[1][name] = $$[$0-1][name]; - } - } - -break; -case 6: yy.actionInclude += $$[$0-1]; this.$ = $$[$0]; -break; -case 7: yy.actionInclude = ''; this.$ = [null,null]; -break; -case 8: this.$ = [$$[$0-1], $$[$0]]; -break; -case 9: this.$ = $$[$0]; -break; -case 10: this.$ = $$[$0]; -break; -case 11: this.$ = {}; this.$[$$[$0]] = 0; -break; -case 12: this.$ = $$[$0-1]; this.$[$$[$0]] = 0; -break; -case 13: this.$ = {}; this.$[$$[$0]] = 1; -break; -case 14: this.$ = $$[$0-1]; this.$[$$[$0]] = 1; -break; -case 15: this.$ = $$[$0-1]; this.$.push($$[$0]); -break; -case 16: this.$ = [$$[$0]]; -break; -case 17: this.$ = $$[$0-2] ? [$$[$0-2], $$[$0-1], $$[$0]] : [$$[$0-1],$$[$0]]; -break; -case 18:this.$ = $$[$0-1]; -break; -case 19:this.$ = $$[$0]; -break; -case 20:this.$ = ''; -break; -case 21:this.$ = yytext; -break; -case 22:this.$ = $$[$0-4]+$$[$0-3]+$$[$0-2]+$$[$0-1]+$$[$0]; -break; -case 23:this.$ = $$[$0-3]+$$[$0-2]+$$[$0-1]+$$[$0]; -break; -case 24: this.$ = $$[$0-1]; -break; -case 25: this.$ = ['*']; -break; -case 27: this.$ = [$$[$0]]; -break; -case 28: this.$ = $$[$0-2]; this.$.push($$[$0]); -break; -case 29: this.$ = $$[$0]; - if (!(yy.options && yy.options.flex) && this.$.match(/[\w\d]$/) && !this.$.match(/\\(b|c[A-Z]|x[0-9A-F]{2}|u[a-fA-F0-9]{4}|[0-7]{1,3})$/)) - this.$ += "\\b"; - -break; -case 30: this.$ = $$[$0-2]+'|'+$$[$0]; -break; -case 31: this.$ = $$[$0-1]+'|'; -break; -case 33: this.$ = '' -break; -case 34: this.$ = $$[$0-1]+$$[$0]; -break; -case 36: this.$ = '('+$$[$0-1]+')'; -break; -case 37: this.$ = $$[$0-2]+$$[$0-1]+')'; -break; -case 38: this.$ = $$[$0-1]+'+'; -break; -case 39: this.$ = $$[$0-1]+'*'; -break; -case 40: this.$ = $$[$0-1]+'?'; -break; -case 41: this.$ = '(?='+$$[$0]+')'; -break; -case 42: this.$ = '(?!'+$$[$0]+')'; -break; -case 44: this.$ = $$[$0-1]+$$[$0]; -break; -case 46: this.$ = '.'; -break; -case 47: this.$ = '^'; -break; -case 48: this.$ = '$'; -break; -case 52: this.$ = yytext; -break; -case 53: this.$ = yytext; -break; -case 54: this.$ = yytext; -break; -case 55: this.$ = yy.prepareString(yytext.substr(1, yytext.length-2)); -break; -} -}, -table: [{3:1,4:2,5:[2,7],10:3,11:[1,4],12:[1,5],14:[1,6],16:[1,7]},{1:[3]},{5:[1,8]},{4:9,5:[2,7],10:3,11:[1,4],12:[1,5],14:[1,6],16:[1,7]},{4:10,5:[2,7],10:3,11:[1,4],12:[1,5],14:[1,6],16:[1,7]},{5:[2,33],11:[2,33],12:[2,33],13:11,14:[2,33],16:[2,33],31:12,32:[2,33],33:13,34:14,35:[1,15],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{15:31,18:[1,32]},{17:33,18:[1,34]},{6:35,11:[2,26],19:36,20:37,22:[2,26],26:[1,38],32:[2,26],35:[2,26],37:[2,26],40:[2,26],41:[2,26],45:[2,26],46:[2,26],47:[2,26],50:[2,26],51:[2,26],52:[2,26],54:[2,26],55:[2,26]},{5:[2,5]},{5:[2,6]},{5:[2,8],11:[2,8],12:[2,8],14:[2,8],16:[2,8]},{5:[2,29],11:[2,29],12:[2,29],14:[2,29],16:[2,29],22:[2,29],32:[1,39]},{5:[2,32],11:[2,32],12:[2,32],14:[2,32],16:[2,32],22:[2,32],32:[2,32],34:40,35:[1,15],36:[2,32],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{5:[2,35],11:[2,35],12:[2,35],14:[2,35],16:[2,35],22:[2,35],29:[1,42],32:[2,35],35:[2,35],36:[2,35],37:[2,35],38:[1,41],39:[1,43],40:[2,35],41:[2,35],43:44,45:[2,35],46:[2,35],47:[2,35],50:[2,35],51:[2,35],52:[2,35],53:[1,45],54:[2,35],55:[2,35]},{31:46,32:[2,33],33:13,34:14,35:[1,15],36:[2,33],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{31:47,32:[2,33],33:13,34:14,35:[1,15],36:[2,33],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{34:48,35:[1,15],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{34:49,35:[1,15],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{5:[2,43],11:[2,43],12:[2,43],14:[2,43],16:[2,43],22:[2,43],29:[2,43],32:[2,43],35:[2,43],36:[2,43],37:[2,43],38:[2,43],39:[2,43],40:[2,43],41:[2,43],45:[2,43],46:[2,43],47:[2,43],50:[2,43],51:[2,43],52:[2,43],53:[2,43],54:[2,43],55:[2,43]},{5:[2,45],11:[2,45],12:[2,45],14:[2,45],16:[2,45],22:[2,45],29:[2,45],32:[2,45],35:[2,45],36:[2,45],37:[2,45],38:[2,45],39:[2,45],40:[2,45],41:[2,45],45:[2,45],46:[2,45],47:[2,45],50:[2,45],51:[2,45],52:[2,45],53:[2,45],54:[2,45],55:[2,45]},{5:[2,46],11:[2,46],12:[2,46],14:[2,46],16:[2,46],22:[2,46],29:[2,46],32:[2,46],35:[2,46],36:[2,46],37:[2,46],38:[2,46],39:[2,46],40:[2,46],41:[2,46],45:[2,46],46:[2,46],47:[2,46],50:[2,46],51:[2,46],52:[2,46],53:[2,46],54:[2,46],55:[2,46]},{5:[2,47],11:[2,47],12:[2,47],14:[2,47],16:[2,47],22:[2,47],29:[2,47],32:[2,47],35:[2,47],36:[2,47],37:[2,47],38:[2,47],39:[2,47],40:[2,47],41:[2,47],45:[2,47],46:[2,47],47:[2,47],50:[2,47],51:[2,47],52:[2,47],53:[2,47],54:[2,47],55:[2,47]},{5:[2,48],11:[2,48],12:[2,48],14:[2,48],16:[2,48],22:[2,48],29:[2,48],32:[2,48],35:[2,48],36:[2,48],37:[2,48],38:[2,48],39:[2,48],40:[2,48],41:[2,48],45:[2,48],46:[2,48],47:[2,48],50:[2,48],51:[2,48],52:[2,48],53:[2,48],54:[2,48],55:[2,48]},{5:[2,49],11:[2,49],12:[2,49],14:[2,49],16:[2,49],22:[2,49],29:[2,49],32:[2,49],35:[2,49],36:[2,49],37:[2,49],38:[2,49],39:[2,49],40:[2,49],41:[2,49],45:[2,49],46:[2,49],47:[2,49],50:[2,49],51:[2,49],52:[2,49],53:[2,49],54:[2,49],55:[2,49]},{5:[2,50],11:[2,50],12:[2,50],14:[2,50],16:[2,50],22:[2,50],29:[2,50],32:[2,50],35:[2,50],36:[2,50],37:[2,50],38:[2,50],39:[2,50],40:[2,50],41:[2,50],45:[2,50],46:[2,50],47:[2,50],50:[2,50],51:[2,50],52:[2,50],53:[2,50],54:[2,50],55:[2,50]},{5:[2,51],11:[2,51],12:[2,51],14:[2,51],16:[2,51],22:[2,51],29:[2,51],32:[2,51],35:[2,51],36:[2,51],37:[2,51],38:[2,51],39:[2,51],40:[2,51],41:[2,51],45:[2,51],46:[2,51],47:[2,51],50:[2,51],51:[2,51],52:[2,51],53:[2,51],54:[2,51],55:[2,51]},{5:[2,52],11:[2,52],12:[2,52],14:[2,52],16:[2,52],22:[2,52],29:[2,52],32:[2,52],35:[2,52],36:[2,52],37:[2,52],38:[2,52],39:[2,52],40:[2,52],41:[2,52],45:[2,52],46:[2,52],47:[2,52],50:[2,52],51:[2,52],52:[2,52],53:[2,52],54:[2,52],55:[2,52]},{5:[2,55],11:[2,55],12:[2,55],14:[2,55],16:[2,55],22:[2,55],29:[2,55],32:[2,55],35:[2,55],36:[2,55],37:[2,55],38:[2,55],39:[2,55],40:[2,55],41:[2,55],45:[2,55],46:[2,55],47:[2,55],50:[2,55],51:[2,55],52:[2,55],53:[2,55],54:[2,55],55:[2,55]},{5:[2,56],11:[2,56],12:[2,56],14:[2,56],16:[2,56],22:[2,56],29:[2,56],32:[2,56],35:[2,56],36:[2,56],37:[2,56],38:[2,56],39:[2,56],40:[2,56],41:[2,56],45:[2,56],46:[2,56],47:[2,56],50:[2,56],51:[2,56],52:[2,56],53:[2,56],54:[2,56],55:[2,56]},{5:[2,53],11:[2,53],12:[2,53],14:[2,53],16:[2,53],22:[2,53],29:[2,53],32:[2,53],35:[2,53],36:[2,53],37:[2,53],38:[2,53],39:[2,53],40:[2,53],41:[2,53],45:[2,53],46:[2,53],47:[2,53],50:[2,53],51:[2,53],52:[2,53],53:[2,53],54:[2,53],55:[2,53]},{5:[2,9],11:[2,9],12:[2,9],14:[2,9],16:[2,9],18:[1,50]},{5:[2,11],11:[2,11],12:[2,11],14:[2,11],16:[2,11],18:[2,11]},{5:[2,10],11:[2,10],12:[2,10],14:[2,10],16:[2,10],18:[1,51]},{5:[2,13],11:[2,13],12:[2,13],14:[2,13],16:[2,13],18:[2,13]},{5:[1,55],7:52,8:[1,54],11:[2,26],19:53,20:37,22:[2,26],26:[1,38],32:[2,26],35:[2,26],37:[2,26],40:[2,26],41:[2,26],45:[2,26],46:[2,26],47:[2,26],50:[2,26],51:[2,26],52:[2,26],54:[2,26],55:[2,26]},{5:[2,16],8:[2,16],11:[2,16],22:[2,16],26:[2,16],32:[2,16],35:[2,16],37:[2,16],40:[2,16],41:[2,16],45:[2,16],46:[2,16],47:[2,16],50:[2,16],51:[2,16],52:[2,16],54:[2,16],55:[2,16]},{11:[2,33],13:56,22:[2,33],31:12,32:[2,33],33:13,34:14,35:[1,15],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{12:[1,59],27:57,29:[1,58]},{5:[2,31],11:[2,31],12:[2,31],14:[2,31],16:[2,31],22:[2,31],32:[2,31],33:60,34:14,35:[1,15],36:[2,31],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{5:[2,34],11:[2,34],12:[2,34],14:[2,34],16:[2,34],22:[2,34],29:[1,42],32:[2,34],35:[2,34],36:[2,34],37:[2,34],38:[1,41],39:[1,43],40:[2,34],41:[2,34],43:44,45:[2,34],46:[2,34],47:[2,34],50:[2,34],51:[2,34],52:[2,34],53:[1,45],54:[2,34],55:[2,34]},{5:[2,38],11:[2,38],12:[2,38],14:[2,38],16:[2,38],22:[2,38],29:[2,38],32:[2,38],35:[2,38],36:[2,38],37:[2,38],38:[2,38],39:[2,38],40:[2,38],41:[2,38],45:[2,38],46:[2,38],47:[2,38],50:[2,38],51:[2,38],52:[2,38],53:[2,38],54:[2,38],55:[2,38]},{5:[2,39],11:[2,39],12:[2,39],14:[2,39],16:[2,39],22:[2,39],29:[2,39],32:[2,39],35:[2,39],36:[2,39],37:[2,39],38:[2,39],39:[2,39],40:[2,39],41:[2,39],45:[2,39],46:[2,39],47:[2,39],50:[2,39],51:[2,39],52:[2,39],53:[2,39],54:[2,39],55:[2,39]},{5:[2,40],11:[2,40],12:[2,40],14:[2,40],16:[2,40],22:[2,40],29:[2,40],32:[2,40],35:[2,40],36:[2,40],37:[2,40],38:[2,40],39:[2,40],40:[2,40],41:[2,40],45:[2,40],46:[2,40],47:[2,40],50:[2,40],51:[2,40],52:[2,40],53:[2,40],54:[2,40],55:[2,40]},{5:[2,44],11:[2,44],12:[2,44],14:[2,44],16:[2,44],22:[2,44],29:[2,44],32:[2,44],35:[2,44],36:[2,44],37:[2,44],38:[2,44],39:[2,44],40:[2,44],41:[2,44],45:[2,44],46:[2,44],47:[2,44],50:[2,44],51:[2,44],52:[2,44],53:[2,44],54:[2,44],55:[2,44]},{5:[2,54],11:[2,54],12:[2,54],14:[2,54],16:[2,54],22:[2,54],29:[2,54],32:[2,54],35:[2,54],36:[2,54],37:[2,54],38:[2,54],39:[2,54],40:[2,54],41:[2,54],45:[2,54],46:[2,54],47:[2,54],50:[2,54],51:[2,54],52:[2,54],53:[2,54],54:[2,54],55:[2,54]},{32:[1,39],36:[1,61]},{32:[1,39],36:[1,62]},{5:[2,41],11:[2,41],12:[2,41],14:[2,41],16:[2,41],22:[2,41],29:[1,42],32:[2,41],35:[2,41],36:[2,41],37:[2,41],38:[1,41],39:[1,43],40:[2,41],41:[2,41],43:44,45:[2,41],46:[2,41],47:[2,41],50:[2,41],51:[2,41],52:[2,41],53:[1,45],54:[2,41],55:[2,41]},{5:[2,42],11:[2,42],12:[2,42],14:[2,42],16:[2,42],22:[2,42],29:[1,42],32:[2,42],35:[2,42],36:[2,42],37:[2,42],38:[1,41],39:[1,43],40:[2,42],41:[2,42],43:44,45:[2,42],46:[2,42],47:[2,42],50:[2,42],51:[2,42],52:[2,42],53:[1,45],54:[2,42],55:[2,42]},{5:[2,12],11:[2,12],12:[2,12],14:[2,12],16:[2,12],18:[2,12]},{5:[2,14],11:[2,14],12:[2,14],14:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{5:[2,15],8:[2,15],11:[2,15],22:[2,15],26:[2,15],32:[2,15],35:[2,15],37:[2,15],40:[2,15],41:[2,15],45:[2,15],46:[2,15],47:[2,15],50:[2,15],51:[2,15],52:[2,15],54:[2,15],55:[2,15]},{1:[2,2]},{8:[1,63],9:[1,64]},{11:[1,67],21:65,22:[1,66]},{28:[1,68],30:[1,69]},{28:[1,70]},{28:[2,27],30:[2,27]},{5:[2,30],11:[2,30],12:[2,30],14:[2,30],16:[2,30],22:[2,30],32:[2,30],34:40,35:[1,15],36:[2,30],37:[1,16],40:[1,17],41:[1,18],42:19,44:20,45:[1,21],46:[1,22],47:[1,23],48:24,49:25,50:[1,26],51:[1,27],52:[1,30],54:[1,28],55:[1,29]},{5:[2,36],11:[2,36],12:[2,36],14:[2,36],16:[2,36],22:[2,36],29:[2,36],32:[2,36],35:[2,36],36:[2,36],37:[2,36],38:[2,36],39:[2,36],40:[2,36],41:[2,36],45:[2,36],46:[2,36],47:[2,36],50:[2,36],51:[2,36],52:[2,36],53:[2,36],54:[2,36],55:[2,36]},{5:[2,37],11:[2,37],12:[2,37],14:[2,37],16:[2,37],22:[2,37],29:[2,37],32:[2,37],35:[2,37],36:[2,37],37:[2,37],38:[2,37],39:[2,37],40:[2,37],41:[2,37],45:[2,37],46:[2,37],47:[2,37],50:[2,37],51:[2,37],52:[2,37],53:[2,37],54:[2,37],55:[2,37]},{1:[2,3]},{8:[1,71]},{5:[2,17],8:[2,17],11:[2,17],22:[2,17],26:[2,17],32:[2,17],35:[2,17],37:[2,17],40:[2,17],41:[2,17],45:[2,17],46:[2,17],47:[2,17],50:[2,17],51:[2,17],52:[2,17],54:[2,17],55:[2,17]},{22:[2,20],23:72,24:[2,20],25:[1,73]},{5:[2,19],8:[2,19],11:[2,19],22:[2,19],26:[2,19],32:[2,19],35:[2,19],37:[2,19],40:[2,19],41:[2,19],45:[2,19],46:[2,19],47:[2,19],50:[2,19],51:[2,19],52:[2,19],54:[2,19],55:[2,19]},{11:[2,24],22:[2,24],32:[2,24],35:[2,24],37:[2,24],40:[2,24],41:[2,24],45:[2,24],46:[2,24],47:[2,24],50:[2,24],51:[2,24],52:[2,24],54:[2,24],55:[2,24]},{12:[1,74]},{11:[2,25],22:[2,25],32:[2,25],35:[2,25],37:[2,25],40:[2,25],41:[2,25],45:[2,25],46:[2,25],47:[2,25],50:[2,25],51:[2,25],52:[2,25],54:[2,25],55:[2,25]},{1:[2,4]},{22:[1,76],24:[1,75]},{22:[2,21],24:[2,21]},{28:[2,28],30:[2,28]},{5:[2,18],8:[2,18],11:[2,18],22:[2,18],26:[2,18],32:[2,18],35:[2,18],37:[2,18],40:[2,18],41:[2,18],45:[2,18],46:[2,18],47:[2,18],50:[2,18],51:[2,18],52:[2,18],54:[2,18],55:[2,18]},{22:[2,20],23:77,24:[2,20],25:[1,73]},{22:[1,76],24:[1,78]},{22:[2,23],24:[2,23],25:[1,79]},{22:[2,22],24:[2,22]}], -defaultActions: {9:[2,5],10:[2,6],52:[2,1],54:[2,2],63:[2,3],71:[2,4]}, -parseError: function parseError(str, hash) { - throw new Error(str); -}, -parse: function parse(input) { - var self = this, stack = [0], vstack = [null], lstack = [], table = this.table, yytext = "", yylineno = 0, yyleng = 0, recovering = 0, TERROR = 2, EOF = 1; - this.lexer.setInput(input); - this.lexer.yy = this.yy; - this.yy.lexer = this.lexer; - this.yy.parser = this; - if (typeof this.lexer.yylloc == "undefined") - this.lexer.yylloc = {}; - var yyloc = this.lexer.yylloc; - lstack.push(yyloc); - var ranges = this.lexer.options && this.lexer.options.ranges; - if (typeof this.yy.parseError === "function") - this.parseError = this.yy.parseError; - function popStack(n) { - stack.length = stack.length - 2 * n; - vstack.length = vstack.length - n; - lstack.length = lstack.length - n; - } - function lex() { - var token; - token = self.lexer.lex() || 1; - if (typeof token !== "number") { - token = self.symbols_[token] || token; - } - return token; - } - var symbol, preErrorSymbol, state, action, a, r, yyval = {}, p, len, newState, expected; - while (true) { - state = stack[stack.length - 1]; - if (this.defaultActions[state]) { - action = this.defaultActions[state]; - } else { - if (symbol === null || typeof symbol == "undefined") { - symbol = lex(); - } - action = table[state] && table[state][symbol]; - } - if (typeof action === "undefined" || !action.length || !action[0]) { - var errStr = ""; - if (!recovering) { - expected = []; - for (p in table[state]) - if (this.terminals_[p] && p > 2) { - expected.push("'" + this.terminals_[p] + "'"); - } - if (this.lexer.showPosition) { - errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + (this.terminals_[symbol] || symbol) + "'"; - } else { - errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'"); - } - this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected}); - } - } - if (action[0] instanceof Array && action.length > 1) { - throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol); - } - switch (action[0]) { - case 1: - stack.push(symbol); - vstack.push(this.lexer.yytext); - lstack.push(this.lexer.yylloc); - stack.push(action[1]); - symbol = null; - if (!preErrorSymbol) { - yyleng = this.lexer.yyleng; - yytext = this.lexer.yytext; - yylineno = this.lexer.yylineno; - yyloc = this.lexer.yylloc; - if (recovering > 0) - recovering--; - } else { - symbol = preErrorSymbol; - preErrorSymbol = null; - } - break; - case 2: - len = this.productions_[action[1]][1]; - yyval.$ = vstack[vstack.length - len]; - yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column}; - if (ranges) { - yyval._$.range = [lstack[lstack.length - (len || 1)].range[0], lstack[lstack.length - 1].range[1]]; - } - r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack); - if (typeof r !== "undefined") { - return r; - } - if (len) { - stack = stack.slice(0, -1 * len * 2); - vstack = vstack.slice(0, -1 * len); - lstack = lstack.slice(0, -1 * len); - } - stack.push(this.productions_[action[1]][0]); - vstack.push(yyval.$); - lstack.push(yyval._$); - newState = table[stack[stack.length - 2]][stack[stack.length - 1]]; - stack.push(newState); - break; - case 3: - return true; - } - } - return true; -} -}; -/* Jison generated lexer */ -var lexer = (function(){ -var lexer = ({EOF:1, -parseError:function parseError(str, hash) { - if (this.yy.parser) { - this.yy.parser.parseError(str, hash); - } else { - throw new Error(str); - } - }, -setInput:function (input) { - this._input = input; - this._more = this._less = this.done = false; - this.yylineno = this.yyleng = 0; - this.yytext = this.matched = this.match = ''; - this.conditionStack = ['INITIAL']; - this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0}; - if (this.options.ranges) this.yylloc.range = [0,0]; - this.offset = 0; - return this; - }, -input:function () { - var ch = this._input[0]; - this.yytext += ch; - this.yyleng++; - this.offset++; - this.match += ch; - this.matched += ch; - var lines = ch.match(/(?:\r\n?|\n).*/g); - if (lines) { - this.yylineno++; - this.yylloc.last_line++; - } else { - this.yylloc.last_column++; - } - if (this.options.ranges) this.yylloc.range[1]++; - - this._input = this._input.slice(1); - return ch; - }, -unput:function (ch) { - var len = ch.length; - var lines = ch.split(/(?:\r\n?|\n)/g); - - this._input = ch + this._input; - this.yytext = this.yytext.substr(0, this.yytext.length-len-1); - //this.yyleng -= len; - this.offset -= len; - var oldLines = this.match.split(/(?:\r\n?|\n)/g); - this.match = this.match.substr(0, this.match.length-1); - this.matched = this.matched.substr(0, this.matched.length-1); - - if (lines.length-1) this.yylineno -= lines.length-1; - var r = this.yylloc.range; - - this.yylloc = {first_line: this.yylloc.first_line, - last_line: this.yylineno+1, - first_column: this.yylloc.first_column, - last_column: lines ? - (lines.length === oldLines.length ? this.yylloc.first_column : 0) + oldLines[oldLines.length - lines.length].length - lines[0].length: - this.yylloc.first_column - len - }; - - if (this.options.ranges) { - this.yylloc.range = [r[0], r[0] + this.yyleng - len]; - } - return this; - }, -more:function () { - this._more = true; - return this; - }, -less:function (n) { - this.unput(this.match.slice(n)); - }, -pastInput:function () { - var past = this.matched.substr(0, this.matched.length - this.match.length); - return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, ""); - }, -upcomingInput:function () { - var next = this.match; - if (next.length < 20) { - next += this._input.substr(0, 20-next.length); - } - return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, ""); - }, -showPosition:function () { - var pre = this.pastInput(); - var c = new Array(pre.length + 1).join("-"); - return pre + this.upcomingInput() + "\n" + c+"^"; - }, -next:function () { - if (this.done) { - return this.EOF; - } - if (!this._input) this.done = true; - - var token, - match, - tempMatch, - index, - col, - lines; - if (!this._more) { - this.yytext = ''; - this.match = ''; - } - var rules = this._currentRules(); - for (var i=0;i < rules.length; i++) { - tempMatch = this._input.match(this.rules[rules[i]]); - if (tempMatch && (!match || tempMatch[0].length > match[0].length)) { - match = tempMatch; - index = i; - if (!this.options.flex) break; - } - } - if (match) { - lines = match[0].match(/(?:\r\n?|\n).*/g); - if (lines) this.yylineno += lines.length; - this.yylloc = {first_line: this.yylloc.last_line, - last_line: this.yylineno+1, - first_column: this.yylloc.last_column, - last_column: lines ? lines[lines.length-1].length-lines[lines.length-1].match(/\r?\n?/)[0].length : this.yylloc.last_column + match[0].length}; - this.yytext += match[0]; - this.match += match[0]; - this.matches = match; - this.yyleng = this.yytext.length; - if (this.options.ranges) { - this.yylloc.range = [this.offset, this.offset += this.yyleng]; - } - this._more = false; - this._input = this._input.slice(match[0].length); - this.matched += match[0]; - token = this.performAction.call(this, this.yy, this, rules[index],this.conditionStack[this.conditionStack.length-1]); - if (this.done && this._input) this.done = false; - if (token) return token; - else return; - } - if (this._input === "") { - return this.EOF; - } else { - return this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(), - {text: "", token: null, line: this.yylineno}); - } - }, -lex:function lex() { - var r = this.next(); - if (typeof r !== 'undefined') { - return r; - } else { - return this.lex(); - } - }, -begin:function begin(condition) { - this.conditionStack.push(condition); - }, -popState:function popState() { - return this.conditionStack.pop(); - }, -_currentRules:function _currentRules() { - return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules; - }, -topState:function () { - return this.conditionStack[this.conditionStack.length-2]; - }, -pushState:function begin(condition) { - this.begin(condition); - }}); -lexer.options = {}; -lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) { - -var YYSTATE=YY_START -switch($avoiding_name_collisions) { -case 0:return 25 -break; -case 1:yy.depth++; return 22 -break; -case 2:yy.depth == 0 ? this.begin('trail') : yy.depth--; return 24 -break; -case 3:return 12 -break; -case 4:this.popState(); return 28 -break; -case 5:return 30 -break; -case 6:return 29 -break; -case 7:/* */ -break; -case 8:this.begin('indented') -break; -case 9:this.begin('code'); return 5 -break; -case 10:return 55 -break; -case 11:yy.options[yy_.yytext] = true -break; -case 12:this.begin('INITIAL') -break; -case 13:this.begin('INITIAL') -break; -case 14:/* empty */ -break; -case 15:return 18 -break; -case 16:this.begin('INITIAL') -break; -case 17:this.begin('INITIAL') -break; -case 18:/* empty */ -break; -case 19:this.begin('rules') -break; -case 20:yy.depth = 0; this.begin('action'); return 22 -break; -case 21:this.begin('trail'); yy_.yytext = yy_.yytext.substr(2, yy_.yytext.length-4);return 11 -break; -case 22:yy_.yytext = yy_.yytext.substr(2, yy_.yytext.length-4); return 11 -break; -case 23:this.begin('rules'); return 11 -break; -case 24:/* ignore */ -break; -case 25:/* ignore */ -break; -case 26:/* */ -break; -case 27:/* */ -break; -case 28:return 12 -break; -case 29:yy_.yytext = yy_.yytext.replace(/\\"/g,'"');return 54 -break; -case 30:yy_.yytext = yy_.yytext.replace(/\\'/g,"'");return 54 -break; -case 31:return 32 -break; -case 32:return 51 -break; -case 33:return 37 -break; -case 34:return 37 -break; -case 35:return 37 -break; -case 36:return 35 -break; -case 37:return 36 -break; -case 38:return 38 -break; -case 39:return 29 -break; -case 40:return 39 -break; -case 41:return 46 -break; -case 42:return 30 -break; -case 43:return 47 -break; -case 44:this.begin('conditions'); return 26 -break; -case 45:return 41 -break; -case 46:return 40 -break; -case 47:return 52 -break; -case 48:yy_.yytext = yy_.yytext.replace(/^\\/g,''); return 52 -break; -case 49:return 47 -break; -case 50:return 45 -break; -case 51:yy.options = {}; this.begin('options') -break; -case 52:this.begin('start_condition');return 14 -break; -case 53:this.begin('start_condition');return 16 -break; -case 54:this.begin('rules'); return 5 -break; -case 55:return 53 -break; -case 56:return 50 -break; -case 57:return 22 -break; -case 58:return 24 -break; -case 59:/* ignore bad characters */ -break; -case 60:return 8 -break; -case 61:return 9 -break; -} -}; -lexer.rules = [/^(?:[^{}]+)/,/^(?:\{)/,/^(?:\})/,/^(?:([a-zA-Z_][a-zA-Z0-9_-]*))/,/^(?:>)/,/^(?:,)/,/^(?:\*)/,/^(?:\n+)/,/^(?:\s+)/,/^(?:%%)/,/^(?:[a-zA-Z0-9_]+)/,/^(?:([a-zA-Z_][a-zA-Z0-9_-]*))/,/^(?:\n+)/,/^(?:\s+\n+)/,/^(?:\s+)/,/^(?:([a-zA-Z_][a-zA-Z0-9_-]*))/,/^(?:\n+)/,/^(?:\s+\n+)/,/^(?:\s+)/,/^(?:.*\n+)/,/^(?:\{)/,/^(?:%\{(.|\n)*?%\})/,/^(?:%\{(.|\n)*?%\})/,/^(?:.+)/,/^(?:\/\*(.|\n|\r)*?\*\/)/,/^(?:\/\/.*)/,/^(?:\n+)/,/^(?:\s+)/,/^(?:([a-zA-Z_][a-zA-Z0-9_-]*))/,/^(?:"(\\\\|\\"|[^"])*")/,/^(?:'(\\\\|\\'|[^'])*')/,/^(?:\|)/,/^(?:\[(\\\\|\\\]|[^\]])*\])/,/^(?:\(\?:)/,/^(?:\(\?=)/,/^(?:\(\?!)/,/^(?:\()/,/^(?:\))/,/^(?:\+)/,/^(?:\*)/,/^(?:\?)/,/^(?:\^)/,/^(?:,)/,/^(?:<>)/,/^(?:<)/,/^(?:\/!)/,/^(?:\/)/,/^(?:\\([0-7]{1,3}|[rfntvsSbBwWdD\\*+()${}|[\]\/.^?]|c[A-Z]|x[0-9A-F]{2}|u[a-fA-F0-9]{4}))/,/^(?:\\.)/,/^(?:\$)/,/^(?:\.)/,/^(?:%options\b)/,/^(?:%s\b)/,/^(?:%x\b)/,/^(?:%%)/,/^(?:\{\d+(,\s?\d+|,)?\})/,/^(?:\{([a-zA-Z_][a-zA-Z0-9_-]*)\})/,/^(?:\{)/,/^(?:\})/,/^(?:.)/,/^(?:$)/,/^(?:(.|\n)+)/]; -lexer.conditions = {"code":{"rules":[60,61],"inclusive":false},"start_condition":{"rules":[15,16,17,18,60],"inclusive":false},"options":{"rules":[11,12,13,14,60],"inclusive":false},"conditions":{"rules":[3,4,5,6,60],"inclusive":false},"action":{"rules":[0,1,2,60],"inclusive":false},"indented":{"rules":[20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"inclusive":true},"trail":{"rules":[19,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"inclusive":true},"rules":{"rules":[7,8,9,10,22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"inclusive":true},"INITIAL":{"rules":[22,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60],"inclusive":true}}; - - -; -return lexer;})() -parser.lexer = lexer; -function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser; -return new Parser; -})(); -if (typeof require !== 'undefined' && typeof exports !== 'undefined') { -exports.parser = jisonlex; -exports.Parser = jisonlex.Parser; -exports.parse = function () { return jisonlex.parse.apply(jisonlex, arguments); } -exports.main = function commonjsMain(args) { - if (!args[1]) - throw new Error('Usage: '+args[0]+' FILE'); - var source, cwd; - if (typeof process !== 'undefined') { - source = require("fs").readFileSync(require("path").resolve(args[1]), "utf8"); - } else { - source = require("file").path(require("file").cwd()).join(args[1]).read({charset: "utf-8"}); - } - return exports.parser.parse(source); -} -if (typeof module !== 'undefined' && require.main === module) { - exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args); -} -} -//*/ -},requires:["fs","path","file","file","system"]});; -return require; -})(); \ No newline at end of file diff --git a/lib/inventory_fallback.js b/lib/inventory_fallback.js deleted file mode 100644 index ec4072c8..00000000 --- a/lib/inventory_fallback.js +++ /dev/null @@ -1,94 +0,0 @@ -/* - Steam inventories are a mess, adds a fallback onto a deprecated inventory endpoint - - We effectively just override some of the inventory URL functions - */ - -function CInventory_GetInventoryLoadURL_CSGOFloat() { - if (g_InventoryFallbackCSGOFloat) { - return `https://steamcommunity.com/profiles/${this.m_steamid}/inventory/json/${this.m_appid}/${this.m_contextid}`; - } else { - /* Fallback to the upstream method */ - return this.g_GetInventoryLoadURL(); - } -} - -function CInventory_AddInventoryData_CSGOFloat(data) { - if (!g_InventoryFallbackCSGOFloat) { - /* upstream can handle */ - return this.g_AddInventoryData(data); - } - - /* Preprocess the data to match the other inventory format */ - if (!data || !data.success) { - alert('failed to fetch inventory'); - return; - } - - const assets = Object.values(data.rgInventory).map(asset => { - return { - appid: this.m_appid, - contextid: this.m_contextid, - assetid: asset.id, - classid: asset.classid, - instanceid: asset.instanceid, - amount: asset.amount, - m_pos: asset.pos, - }; - }).sort((a, b) => a.m_pos - b.m_pos); - - const transformedData = { - assets, - descriptions: Object.values(data.rgDescriptions), - total_inventory_count: Math.max(...assets.map(e => e.m_pos)), - success: true, - more_items: 0, - rwgrsn: -2 - }; - - /* Required to force the page to lazy load images correctly */ - this.m_bNeedsRepagination = true; - - return this.g_AddInventoryData(transformedData); -} - -function CInventory_ShowInventoryLoadError_CSGOFloat() { - const prev_$ErrorDisplay = this.m_$ErrorDisplay; - - /* Handle upstream like before */ - this.g_ShowInventoryLoadError(); - - if (prev_$ErrorDisplay) { - /* Element already created, nothing special to do */ - return; - } - - this.m_$ErrorDisplay.find(".retry_load_btn").after(` -
- Try Again using CSGOFloat -
- `); - this.m_$ErrorDisplay.find(".retry_load_btn_csgofloat").click(() => { - g_InventoryFallbackCSGOFloat = true; - this.RetryLoad() - }); -} - -let fallbackScript = document.createElement('script'); -fallbackScript.innerText = ` - g_InventoryFallbackCSGOFloat = false; - /* Keep old func references */ - CInventory.prototype.g_GetInventoryLoadURL = CInventory.prototype.GetInventoryLoadURL; - CInventory.prototype.g_AddInventoryData = CInventory.prototype.AddInventoryData; - CInventory.prototype.g_ShowInventoryLoadError = CInventory.prototype.ShowInventoryLoadError; - - ${CInventory_GetInventoryLoadURL_CSGOFloat.toString()} - ${CInventory_AddInventoryData_CSGOFloat.toString()} - ${CInventory_ShowInventoryLoadError_CSGOFloat.toString()} - - CInventory.prototype.GetInventoryLoadURL = CInventory_GetInventoryLoadURL_CSGOFloat; - CInventory.prototype.AddInventoryData = CInventory_AddInventoryData_CSGOFloat; - CInventory.prototype.ShowInventoryLoadError = CInventory_ShowInventoryLoadError_CSGOFloat; -`; - -document.head.appendChild(fallbackScript); diff --git a/lib/queue.js b/lib/queue.js deleted file mode 100644 index a7877523..00000000 --- a/lib/queue.js +++ /dev/null @@ -1,112 +0,0 @@ - -class Queue { - constructor() { - this.queue = []; - this.failedRequests = []; - this.running = false; - this.concurrency = 10; - this.processing = 0; - } - - addJob(link, listingId, force) { - if (listingId in floatData) { - showFloat(listingId); - return; - } - - const job = { - link, - listingId - }; - - // Stop if this item is already in the queue - if (this.queue.find(j => j.listingId === listingId)) { - return; - } - - if (!force) { - // Prevent us from auto-fetching a previously failed request unless it's a user action - if (this.failedRequests.find(v => v === listingId)) { - return; - } - } - - const promise = new Promise((resolve, reject) => { - job.resolve = resolve; - job.reject = reject; - }); - - this.queue.push(job); - this.checkQueue(); - - return promise; - } - - async checkQueue() { - if (!this.running) return; - - if (this.queue.length > 0 && this.processing < this.concurrency) { - // there is a free bot, process the job - let job = this.queue.shift(); - - this.processing += 1; - - const floatDiv = document.querySelector(`#item_${job.listingId}_floatdiv`); - - // Changed pages or div not visible, cancel request - if (!floatDiv || floatDiv.offsetParent === null) { - this.processing -= 1; - this.checkQueue(); - return; - } - - const buttonText = floatDiv.querySelector('#getFloatBtn span'); - if (buttonText) { - buttonText.fetching = true; - buttonText.innerText = 'Fetching'; - } - - const params = { inspectLink: job.link }; - - if (isMarketListing(job.listingId)) { - const listingData = (await retrieveListingInfoFromPage(job.listingId))[job.listingId]; - if (listingData.currencyid === 2001) { - params.listPrice = listingData.price + listingData.fee; - } else if (listingData.converted_currencyid === 2001) { - params.listPrice = listingData.converted_price + listingData.converted_fee; - } - } - - const data = await sendMessage(params); - - if (buttonText) { - buttonText.fetching = false; - } - - if (data && data.iteminfo) { - floatData[job.listingId] = data.iteminfo; - showFloat(job.listingId); - } else { - // Reset the button text for this itemid - if (buttonText) buttonText.innerText = 'Get Float'; - - // Change the message div for this item to the error - if (floatDiv && floatDiv.querySelector('.floatmessage')) { - floatDiv.querySelector('.floatmessage').innerText = data.error || 'Unknown Error'; - } - - this.failedRequests.push(job.listingId); - } - - this.processing -= 1; - this.checkQueue(); - } - } - - start() { - if (!this.running) { - this.running = true; - this.checkQueue(); - } - } -} diff --git a/lib/stall.js b/lib/stall.js deleted file mode 100644 index bf0770d4..00000000 --- a/lib/stall.js +++ /dev/null @@ -1,29 +0,0 @@ -class StallFetcher { - constructor() { - // Maps string -> stall items - this.stalls = {}; - } - - async getStallItem(steamId, itemId) { - if (this.stalls[steamId]) { - return this.stalls[steamId].listings.find(e => (e.offering || e.item).asset_id === itemId); - } - - let stall; - - try { - stall = await sendMessage({ stall: true, steamId}); - if (!stall.listings) { - // Stub out to prevent further calls - stall = {listings: []}; - } - } catch (e) { - return; - } - - this.stalls[steamId] = stall; - return stall.listings.find(e => (e.offering || e.item).asset_id === itemId); - } -} - -const stallFetcher = new StallFetcher(); diff --git a/lib/time_fetcher.js b/lib/time_fetcher.js deleted file mode 100644 index 60eb6032..00000000 --- a/lib/time_fetcher.js +++ /dev/null @@ -1,171 +0,0 @@ -function historyRowHashcode_CSGOFloat(row) { - const text = row.innerText.replace(/\W/g, ''); - - /* Based on https://stackoverflow.com/a/8831937 (Java's hashCode() method) */ - let hash = 0; - if (text.length === 0) { - return hash; - } - for (let i = 0; i < text.length; i++) { - const char = text.charCodeAt(i); - hash = ((hash<<5)-hash)+char; - hash = hash & hash; - } - - return hash; -} - -function getTimestampFromTrade_CSGOFloat(row) { - const dateDiv = row.querySelector('.tradehistory_date'); - const date = dateDiv.firstChild.nodeValue.trim(); - const time = dateDiv.querySelector('.tradehistory_timestamp').innerText; - - const d = new Date(date); - const pure = time.replace('am', '').replace('pm', ''); - let hours = parseInt(pure.split(':')[0]); - const minutes = parseInt(pure.split(':')[1]); - if (time.includes('pm') && hours !== 12) { - /* Prevent 12:XXpm from getting 12 hours added */ - hours += 12 - } else if (time.includes('am') && hours === 12) { - /* Prevent 12:XXam from getting 12 hours instead of being 0 */ - hours -= 12 - } - - d.setHours(hours); - d.setMinutes(minutes); - return d.getTime() / 1000; -} - -async function addVerifyButtons() { - let rows = document.querySelectorAll('.tradehistoryrow'); - - for (const [i, row] of rows.entries()) { - const btnId = `verify_${i}_csgofloat`; - - if (row.querySelector(`#${btnId}`)) { - // Already placed the button - continue; - } - - let proveBtn = createButton('CSGOFloat Proof', 'green', btnId); - proveBtn.addEventListener('click', () => { - window.postMessage( - { - type: 'fetchTime', - index: i - }, - '*' - ); - }); - - row.querySelector('.tradehistory_content').append(proveBtn); - } -} - -async function hasTradeBeforeTime_CSGOFloat(hashCode, timestamp) { - const resp = await fetch(`${location.protocol}//${location.host}${location.pathname}?after_time=${timestamp}&l=english`, { - credentials: 'same-origin' - }); - - const body = await resp.text(); - - if (body.includes('too many requests')) { - alert('You need to wait a couple seconds before generating the proof due to Valve rate-limits'); - throw 'Too many requests'; - } - - const doc = new DOMParser().parseFromString(body, 'text/html'); - const rows = doc.querySelectorAll('.tradehistoryrow'); - - for (const row of rows) { - - const thisCode = historyRowHashcode_CSGOFloat(row); - if (thisCode === hashCode) { - return true; - } - } - - return false; -} - -async function fetchEnglishRow_CSGOFloat(index) { - let queryParams = location.search; - if (queryParams === '') { - queryParams = '?l=english'; - } else { - queryParams += '&l=english'; - } - - /* Forces us to fetch the english version of the row at a given index no matter what */ - const resp = await fetch(`${location.protocol}//${location.host}${location.pathname}${queryParams}`, { - credentials: 'same-origin' - }); - - const body = await resp.text(); - - const doc = new DOMParser().parseFromString(body, 'text/html'); - const rows = doc.querySelectorAll('.tradehistoryrow'); - return rows[index]; -} - -async function fetchListingTime_CSGOFloat(index) { - const btn = document.querySelector(`#verify_${index}_csgofloat`); - btn.querySelector('span').innerText = 'Computing Proof...'; - - const node = await fetchEnglishRow_CSGOFloat(index); - const hashCode = historyRowHashcode_CSGOFloat(node); - - let timestamp; - - try { - timestamp = getTimestampFromTrade_CSGOFloat(node); - if (!timestamp) { - throw 'failed timestamp creation'; - } - } catch(e) { - console.error(e); - alert("Failed to parse time, make sure you're on an english version of the page by appending ?l=english to the url"); - return; - } - - let left = 0, right = 60; - let amt = 0; - while (left < right && amt < 5) { - const middle = left + Math.floor((right - left) / 2); - const hasTrade = await hasTradeBeforeTime_CSGOFloat(hashCode, timestamp + middle); - if (hasTrade) { - right = middle; - } else { - left = middle; - } - amt++; - } - - /* Hello to all the reversers */ - const proof = timestamp + Math.floor((right + left) / 2); - - const span = document.createElement('span'); - span.innerText = `Proof: ${proof}`; - btn.parentNode.append(span); - btn.parentNode.removeChild(btn); -} - -// register the message listener in the page scope -let script = document.createElement('script'); -script.innerText = ` - ${fetchEnglishRow_CSGOFloat.toString()} - ${hasTradeBeforeTime_CSGOFloat.toString()} - ${fetchListingTime_CSGOFloat.toString()} - ${historyRowHashcode_CSGOFloat.toString()} - ${getTimestampFromTrade_CSGOFloat.toString()} - window.addEventListener('message', (e) => { - if (e.data.type == 'fetchTime') { - fetchListingTime_CSGOFloat(e.data.index); - } - }); -`; - -document.head.appendChild(script); - -addVerifyButtons(); diff --git a/lib/trade_offer.js b/lib/trade_offer.js deleted file mode 100644 index 7c7fe6ca..00000000 --- a/lib/trade_offer.js +++ /dev/null @@ -1,217 +0,0 @@ -const getLogoEl = function () { - const logo = document.createElement('div'); - logo.style.float = 'left'; - const logoImage = document.createElement('img'); - logoImage.src = 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/79/798a12316637ad8fbb91ddb7dc63f770b680bd19_full.jpg'; - logoImage.style.height = '32px'; - logo.appendChild(logoImage); - return logo; -}; - -const createBanner = function (backgroundColor) { - const banner = document.createElement('div'); - banner.style.marginTop = '10px'; - banner.style.marginBottom = '10px'; - banner.style.padding = '15px'; - banner.style.backgroundColor = backgroundColor; - banner.style.color = 'white'; - banner.style.display = 'flex'; - banner.style.justifyContent = 'space-between'; - banner.style.alignItems = 'center'; - return banner; -}; - -const showWarning = function () { - const tradeNode = createBanner('#b30000'); - - const content = document.createElement('div'); - tradeNode.appendChild(content); - - content.appendChild(getLogoEl()); - - const name = document.createElement('span'); - name.style.fontSize = '18px'; - name.style.marginLeft = '15px'; - name.style.lineHeight = '32px'; - name.style.fontWeight = 'bold'; - name.innerText = 'Warning!'; - content.appendChild(name); - - const detected = document.createElement('div'); - detected.style.paddingLeft = '45px'; - detected.style.color = 'darkgrey'; - detected.innerText = `Some of the items in the offer were not purchased from you on CSGOFloat Market (or you're logged into the wrong account)`; - content.appendChild(detected); - - const tradeArea = document.querySelector('.trade_area'); - - tradeArea.parentNode.insertBefore(tradeNode, tradeArea); -}; - -const showValidated = function f() { - const tradeNode = createBanner('#006700'); - - const content = document.createElement('div'); - tradeNode.appendChild(content); - - content.appendChild(getLogoEl()); - - const name = document.createElement('span'); - name.style.fontSize = '18px'; - name.style.marginLeft = '15px'; - name.style.lineHeight = '32px'; - name.style.fontWeight = 'bold'; - name.innerText = 'Trade Validated'; - content.appendChild(name); - - const detected = document.createElement('div'); - detected.style.paddingLeft = '45px'; - detected.style.color = 'darkgrey'; - detected.innerText = `All of the items you're sending correspond to a CSGOFloat purchase`; - detected.style.color = 'darkgrey'; - content.appendChild(detected); - - const tradeArea = document.querySelector('.trade_area'); - - tradeArea.parentNode.insertBefore(tradeNode, tradeArea); -}; - -const addAutoFill = function (trades, tradeStatus, isSeller) { - const type = isSeller ? 'Sale' : 'Purchase'; - - for (const trade of trades) { - // TODO: Remove backwards compatibility once rolled out - const item = trade.contract.offering || trade.contract.item; - - if (tradeStatus.them.assets.find(e => e.assetid == item.asset_id)) { - // They are viewing an offer sent to them that already has the item included - continue; - } - - const tradeNode = createBanner('#303030'); - - const left = document.createElement('div'); - tradeNode.appendChild(left); - - left.appendChild(getLogoEl()); - - const name = document.createElement('span'); - name.style.fontSize = '18px'; - name.style.marginLeft = '15px'; - name.style.lineHeight = '32px'; - name.innerText = item.market_hash_name; - left.appendChild(name); - - const detected = document.createElement('div'); - detected.style.paddingLeft = '45px'; - detected.style.color = 'darkgrey'; - detected.innerText = `Detected ${type} (Float: ${item.float_value.toFixed(12)}, Seed: ${item.paint_seed})`; - detected.style.color = 'darkgrey'; - left.appendChild(detected); - - /* right */ - const right = document.createElement('div'); - - const autoFill = createButton('Auto-Fill', 'green'); - autoFill.addEventListener('click', () => { - window.postMessage( - { - type: 'autoFill', - id: item.asset_id, - isSeller - }, - '*' - ); - - document.getElementById('trade_offer_note').value = `CSGOFloat Market Trade Offer #${trade.id} \n\nThanks for using CSGOFloat!`; - }); - - right.appendChild(autoFill); - tradeNode.appendChild(right); - - const tradeArea = document.querySelector('.trade_area'); - tradeArea.parentNode.insertBefore(tradeNode, tradeArea); - } -}; - -const addFloatMarketFill = async function () { - let data; - try { - data = await sendMessage({ floatMarket: true }); - } catch (e) { - return; - } - - if (!data.trades_to_send) { - // Must be logged out of CSGOFloat - return; - } - - const partnerId = await retrievePartnerInfo(); - - const partnerTrades = data.trades_to_send.filter(e => e.buyer_id === partnerId); - const tradeStatus = await retrieveTradeStatus(); - - if (tradeStatus.me.assets.length > 0) { - let hasAutofillText = false; - - const tradeMessages = document.getElementsByClassName("included_trade_offer_note_ctn"); - if (tradeMessages.length > 0) { - const sanitized = tradeMessages[0].innerText.trim().replace(/ /g, '').toLowerCase(); - - // TODO: Use edit-distance - hasAutofillText = sanitized.includes('csgofloat') || sanitized.includes('floatmarket'); - } - - // Whether all items match a csgofloat purchase from this seller (this account) - let allItemsMatch = true; - - for (const item of tradeStatus.me.assets) { - const hasTrade = partnerTrades.find(e => (e.contract.offering || e.contract.item).asset_id === item.assetid); - - if (!hasTrade) { - allItemsMatch = false; - break; - } - } - - if (allItemsMatch) { - showValidated(); - } else if (hasAutofillText) { - // Only show warning if we find csgofloat related text in the description - // Otherwise we'd show a warning on every non-csgofloat trade offer - showWarning(); - } - } - - addAutoFill(partnerTrades, tradeStatus, true); - - // Auto-fill for buyers - addAutoFill(data.trades_to_receive.filter(e => e.seller_id === partnerId), tradeStatus, false); -}; - -let tScript = document.createElement('script'); -tScript.innerText = ` - window.addEventListener('message', (e) => { - if (e.data.type == 'autoFill') { - if (e.data.isSeller) { - $J('#inventory_select_your_inventory').click(); - MoveItemToTrade(UserYou.findAsset(730, 2, e.data.id).element); - } else { - $J('#inventory_select_their_inventory').click(); - - /* Make sure their inventory is loaded first (it is dynamically loaded when the tab is clicked) */ - const fetchEl = setInterval(() => { - const item = UserThem.findAsset(730, 2, e.data.id); - if (item) { - clearInterval(fetchEl); - MoveItemToTrade(UserThem.findAsset(730, 2, e.data.id).element); - } - }, 250); - } - } - }); -`; - -document.head.appendChild(tScript); - diff --git a/lib/utils.js b/lib/utils.js deleted file mode 100644 index 9b3410a6..00000000 --- a/lib/utils.js +++ /dev/null @@ -1,251 +0,0 @@ -const createButton = function(text, colour, id) { - let btn = document.createElement('a'); - btn.classList.add(`btn_${colour}_white_innerfade`); - btn.classList.add('btn_small'); - btn.classList.add('float-btn'); - - if (id) btn.id = id; - - let span = document.createElement('span'); - span.innerText = text; - btn.appendChild(span); - - return btn; -}; - -const removeAllItemsHtml = function() { - // Iterate through each item on the page - let listingRows = document.querySelectorAll('.market_listing_row.market_recent_listing_row'); - - for (let row of listingRows) { - let id = row.id.replace('listing_', ''); - - let floatDiv = row.querySelector(`#item_${id}_floatdiv`); - - if (floatDiv) { - row.style.backgroundColor = ''; - floatDiv.parentNode.removeChild(floatDiv); - } - - let stickerDiv = row.querySelector('.float-stickers-container'); - if (stickerDiv) { - stickerDiv.parentNode.removeChild(stickerDiv); - } - } -}; - -const hexToRgb = function(hex) { - let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); - return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null; -}; - -const rgbToHex = function(rgb) { - return '#' + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1); -}; - -// Based on https://stackoverflow.com/a/41491220 -const pickTextColour = function(bgColor, lightColour, darkColour) { - const color = bgColor.charAt(0) === '#' ? bgColor.substring(1, 7) : bgColor; - const r = parseInt(color.substring(0, 2), 16); // hexToR - const g = parseInt(color.substring(2, 4), 16); // hexToG - const b = parseInt(color.substring(4, 6), 16); // hexToB - const uicolors = [r / 255, g / 255, b / 255]; - const c = uicolors.map(col => { - if (col <= 0.03928) { - return col / 12.92; - } - return Math.pow((col + 0.055) / 1.055, 2.4); - }); - const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2]; - return L > 0.179 ? darkColour : lightColour; -}; - -const isInventoryPage = function() { - return /^\/(profiles|id)\/\S*\/inventory/.test(window.location.pathname); -}; - -const isTradePage = function () { - return /^\/tradeoffer\/\S*/.test(window.location.pathname); -}; - -const extractInspectAssetId = function(link) { - const m = decodeURIComponent(link).match( - /^steam:\/\/rungame\/730\/\d+\/[+ ]csgo_econ_action_preview [SM]\d+A(\d+)D\d+$/ - ); - return m && m[1]; -}; - -const extractInspectSteamId = function(link) { - const m = decodeURIComponent(link).match( - /^steam:\/\/rungame\/730\/\d+\/[+ ]csgo_econ_action_preview [SM](\d+)A\d+D\d+$/ - ); - return m && m[1]; -}; - -const wearRanges = [ - [0.0, 0.07], - [0.07, 0.15], - [0.15, 0.38], - [0.38, 0.45], - [0.45, 1.0] -]; - -const rangeFromWear = function(wear) { - for (const range of wearRanges) { - if (wear > range[0] && wear <= range[1]) { - return range; - } - } -}; - -const dopplerPhases = { - 418: 'Phase 1', - 419: 'Phase 2', - 420: 'Phase 3', - 421: 'Phase 4', - 415: 'Ruby', - 416: 'Sapphire', - 417: 'Black Pearl', - 569: 'Phase 1', - 570: 'Phase 2', - 571: 'Phase 3', - 572: 'Phase 4', - 568: 'Emerald', - 618: 'Phase 2', - 619: 'Sapphire', - 617: 'Black Pearl', - 852: 'Phase 1', - 853: 'Phase 2', - 854: 'Phase 3', - 855: 'Phase 4', - 1119: "Emerald", - 1120: "Phase 1", - 1121: "Phase 2", - 1122: "Phase 3", - 1123: "Phase 4" -}; - -const hasDopplerPhase = function(paintIndex) { - return paintIndex in dopplerPhases; -}; - -const getDopplerPhase = function (paintIndex) { - return dopplerPhases[paintIndex]; -}; - -/** - * Compares two software version numbers (e.g. "1.7.1" or "1.2b"). - * - * This function was born in http://stackoverflow.com/a/6832721. - * - * @param {string} v1 The first version to be compared. - * @param {string} v2 The second version to be compared. - * @param {object} [options] Optional flags that affect comparison behavior: - *
    - *
  • - * lexicographical: true compares each part of the version strings lexicographically instead of - * naturally; this allows suffixes such as "b" or "dev" but will cause "1.10" to be considered smaller than - * "1.2". - *
  • - *
  • - * zeroExtend: true changes the result if one version string has less parts than the other. In - * this case the shorter string will be padded with "zero" parts instead of being considered smaller. - *
  • - *
- * @returns {number|NaN} - *
    - *
  • 0 if the versions are equal
  • - *
  • a negative integer iff v1 < v2
  • - *
  • a positive integer iff v1 > v2
  • - *
  • NaN if either version string is in the wrong format
  • - *
- * - * @copyright by Jon Papaioannou (["john", "papaioannou"].join(".") + "@gmail.com") - * @license This function is in the public domain. Do what you want with it, no strings attached. - */ -function versionCompare(v1, v2, options) { - var lexicographical = options && options.lexicographical, - zeroExtend = options && options.zeroExtend, - v1parts = v1.split('.'), - v2parts = v2.split('.'); - - function isValidPart(x) { - return (lexicographical ? /^\d+[A-Za-z]*$/ : /^\d+$/).test(x); - } - - if (!v1parts.every(isValidPart) || !v2parts.every(isValidPart)) { - return NaN; - } - - if (zeroExtend) { - while (v1parts.length < v2parts.length) v1parts.push('0'); - while (v2parts.length < v1parts.length) v2parts.push('0'); - } - - if (!lexicographical) { - v1parts = v1parts.map(Number); - v2parts = v2parts.map(Number); - } - - for (var i = 0; i < v1parts.length; ++i) { - if (v2parts.length == i) { - return 1; - } - - if (v1parts[i] == v2parts[i]) { - continue; - } else if (v1parts[i] > v2parts[i]) { - return 1; - } else { - return -1; - } - } - - if (v1parts.length != v2parts.length) { - return -1; - } - - return 0; -} - -const sendMessage = function(params) { - return new Promise(resolve => { - chrome.runtime.sendMessage(params, data => { - resolve(data); - }); - }); -}; - -function getFloatDbCategory(item) { - if (item.full_item_name.includes('StatTrak')) { - return 2; - } else if (item.full_item_name.includes('Souvenir')) { - return 3; - } else { - // "Normal" - return 1; - } -} - -/** - * Gets formatted link for floatdb for the specified item type and order - * @param item item properties dict - * @param order 1 for low float, -1 for high float ordering - */ -function getFloatDbLink(item, order) { - return `https://csgofloat.com/db?defIndex=${item.defindex}&paintIndex=${item.paintindex}&order=${order}&category=${getFloatDbCategory(item)}`; - -} - -const getRankLink = function (itemInfo, rank) { - const link = document.createElement('a'); - link.href = getFloatDbLink(itemInfo, rank === itemInfo.low_rank ? 1 : -1); - link.innerText = ` (Rank #${rank})`; - link.target = '_blank'; - return link; -}; - -const isMarketListing = function (id) { - // Dumb heuristic, has to be larger than asset id - return id >= 10000000000000; -}; diff --git a/manifest.json b/manifest.json index 18e22f32..ea3db52f 100644 --- a/manifest.json +++ b/manifest.json @@ -1,8 +1,8 @@ { - "manifest_version": 2, + "manifest_version": 3, "name": "CSGOFloat Market Checker", "short_name": "CSGOFloat", - "version": "2.4.3", + "version": "3.0.0", "description": "Dedicated API for fetching the float value, paint seed, and screenshots of CS:GO items on the Steam Market or Inventories", "icons": { "16": "icons/16.png", @@ -12,48 +12,61 @@ "content_scripts": [ { "matches": [ - "*://*.steamcommunity.com/market/listings/730/*", - "*://*.steamcommunity.com/id/*/inventory*", - "*://*.steamcommunity.com/profiles/*/inventory*", - "*://*.steamcommunity.com/tradeoffer/*" + "*://*.steamcommunity.com/market/listings/730/*" ], "js": [ - "lib/filtrex.js", - "lib/utils.js", - "lib/filters.js", - "lib/queue.js", - "lib/bridge.js", - "lib/trade_offer.js", - "lib/stall.js", - "lib/inventory_fallback.js", - "lib/banner.js", - "float.js" - ], - "css": ["float.css"] + "src/lib/page_scripts/market_listing.js" + ], + "css": ["src/global.css"] }, { "matches": [ "*://*.steamcommunity.com/id/*/tradehistory*", "*://*.steamcommunity.com/profiles/*/tradehistory*" ], "js": [ - "lib/utils.js", - "lib/time_fetcher.js" + "src/lib/page_scripts/trade_history.js" + ], + "css": ["src/global.css"] + }, { + "matches": [ + "*://*.steamcommunity.com/id/*/inventory*", + "*://*.steamcommunity.com/profiles/*/inventory*" + ], + "js": [ + "src/lib/page_scripts/inventory.js" ], - "css": ["float.css"] + "css": ["src/global.css"] + }, { + "matches": [ + "*://*.steamcommunity.com/tradeoffer/*" + ], + "js": [ + "src/lib/page_scripts/trade_offer.js" + ], + "css": ["src/global.css"] } ], "background": { - "scripts": ["background.js"] + "service_worker": "src/background.js", + "type": "module" }, "permissions": [ + "storage", + "scripting" + ], + "host_permissions": [ "*://*.steamcommunity.com/market/listings/730/*", "*://*.steamcommunity.com/id/*/inventory*", "*://*.steamcommunity.com/id/*/tradehistory*", - "*://*.steamcommunity.com/profiles/*/inventory*", - "storage" + "*://*.steamcommunity.com/profiles/*/inventory*" ], + "externally_connectable" : { + "matches": ["*://*.steamcommunity.com/*"] + }, "web_accessible_resources": [ - "model_frame.html", - "model_frame.js" + { + "resources": ["src/model_frame.html", "src/model_frame.js"], + "matches": [ "https://steamcommunity.com/*" ] + } ] } diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..be6be4c1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4559 @@ +{ + "name": "csgofloat-extension", + "version": "3.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "csgofloat-extension", + "version": "3.0.0", + "license": "MIT", + "devDependencies": { + "@types/chrome": "^0.0.193", + "@types/jquery": "^3.5.14", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.7.1", + "decorator-cache-getter": "^1.0.0", + "file-loader": "^6.2.0", + "filtrex": "^3.0.0", + "glob": "^8.0.3", + "html-loader": "^4.1.0", + "ignore-loader": "^0.1.2", + "lit": "^2.3.0", + "lit-html": "^2.3.1", + "lodash": "^4.17.21", + "lodash-decorators": "^6.0.1", + "mini-css-extract-plugin": "^2.6.1", + "rxjs": "^7.5.7", + "sass-loader": "^13.0.2", + "ts-loader": "^9.3.1", + "typescript": "^4.7.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@lit/reactive-element": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.0.tgz", + "integrity": "sha512-blrtlLKvtVyjTJ3gUHWNSHOU6tD8be9mRafqtnO7GVMcB+5z4RjNcO0DpMGmccK6N8yur1vVVYnS0gPdQ/WgEQ==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@types/chrome": { + "version": "0.0.193", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.193.tgz", + "integrity": "sha512-R8C84oqvk8A8C8G1viBd8qLpDr86Y/jwD+KLgzUekbIT9RGds6a9GnlQyg8P7ltnGogTMHkiEQK0ZlcrvTeo3Q==", + "dev": true, + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "node_modules/@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "node_modules/@types/har-format": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.8.tgz", + "integrity": "sha512-OP6L9VuZNdskgNN3zFQQ54ceYD8OLq5IbqO4VK91ORLfOm7WdT/CiT/pHEBSQEqCInJ2y3O6iCm/zGtPElpgJQ==", + "dev": true + }, + "node_modules/@types/jquery": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", + "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", + "dev": true, + "dependencies": { + "@types/sizzle": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.3.tgz", + "integrity": "sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "node_modules/@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", + "dev": true + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001373", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz", + "integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dev": true, + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/decorator-cache-getter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/decorator-cache-getter/-/decorator-cache-getter-1.0.0.tgz", + "integrity": "sha512-yB49c5qi0govRjpe18vutEkkKzosIt2XggYSs1Qyev1TJYTh2WmLgDp0dV6VJ/6yNBRlKG+qMG80Vy4Bg0mLJw==", + "dev": true + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.210", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.210.tgz", + "integrity": "sha512-kSiX4tuyZijV7Cz0MWVmGT8K2siqaOA4Z66K5dCttPPRh0HicOcOAEj1KlC8O8J1aOS/1M8rGofOzksLKaHWcQ==", + "dev": true + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true, + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/filtrex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filtrex/-/filtrex-3.0.0.tgz", + "integrity": "sha512-7PUty9Bq+shxb6r1QVo0AmwJVY59HNN76YUD5TIkjtyG6nNhWPTe6dJVq4TKaGdl/V329ayG6rHK1sA31Hmz8Q==", + "dev": true + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-4.1.0.tgz", + "integrity": "sha512-QDDNmLgn96NWtTPx/VXRerFXH0hn7cm4bruqsZ333GCb+rqiqGurcxtP/M52wcui1/iLiu0l5ms/McE7/Ik6aQ==", + "dev": true, + "dependencies": { + "html-minifier-terser": "^6.1.0", + "parse5": "^7.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-minifier-terser/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-loader": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", + "integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==", + "dev": true + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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, + "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, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/lit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.3.0.tgz", + "integrity": "sha512-ynSGsUYKSGN2weFQ1F3SZq0Ihlj+vr/3KAET//Yf8Tz86L7lZizlw9Px+ab5iN8Si4RkVoLqd9YtKQmjdyKHNg==", + "dev": true, + "dependencies": { + "@lit/reactive-element": "^1.4.0", + "lit-element": "^3.2.0", + "lit-html": "^2.3.0" + } + }, + "node_modules/lit-element": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", + "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", + "dev": true, + "dependencies": { + "@lit/reactive-element": "^1.3.0", + "lit-html": "^2.2.0" + } + }, + "node_modules/lit-html": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.3.1.tgz", + "integrity": "sha512-FyKH6LTW6aBdkfNhNSHyZTnLgJSTe5hMk7HFtc/+DcN1w74C215q8B+Cfxc2OuIEpBNcEKxgF64qL8as30FDHA==", + "dev": true, + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-decorators": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/lodash-decorators/-/lodash-decorators-6.0.1.tgz", + "integrity": "sha512-1M0YC8G3nFTkejZEk2ehyvryEdcqj6xATH+ybI8j53cLs/bKRsavaE//y7nz/A0vxEFhxYqev7vdWfsuTJ1AtQ==", + "dev": true, + "dependencies": { + "tslib": "^1.9.2" + }, + "engines": { + "node": ">=0.12.0" + }, + "peerDependencies": { + "lodash": "4.x" + } + }, + "node_modules/lodash-decorators/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/mini-css-extract-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/mini-css-extract-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "dependencies": { + "entities": "^4.3.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "dependencies": { + "icss-utils": "^5.0.0" + }, + "engines": { + "node": "^10 || ^12 || >= 14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "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 + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "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" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "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" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "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" + } + ] + }, + "node_modules/sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "fibers": ">= 3.1.0", + "node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + }, + "dependencies": { + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.14", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz", + "integrity": "sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@lit/reactive-element": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.4.0.tgz", + "integrity": "sha512-blrtlLKvtVyjTJ3gUHWNSHOU6tD8be9mRafqtnO7GVMcB+5z4RjNcO0DpMGmccK6N8yur1vVVYnS0gPdQ/WgEQ==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@types/chrome": { + "version": "0.0.193", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.193.tgz", + "integrity": "sha512-R8C84oqvk8A8C8G1viBd8qLpDr86Y/jwD+KLgzUekbIT9RGds6a9GnlQyg8P7ltnGogTMHkiEQK0ZlcrvTeo3Q==", + "dev": true, + "requires": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "@types/eslint": { + "version": "8.4.5", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.5.tgz", + "integrity": "sha512-dhsC09y1gpJWnK+Ff4SGvCuSnk9DaU0BJZSzOwa6GVSg65XtTugLBITDAAzRU5duGBoXBHpdR/9jHGxJjNflJQ==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "0.0.51", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.51.tgz", + "integrity": "sha512-CuPgU6f3eT/XgKKPqKd/gLZV1Xmvf1a2R5POBOGQa6uv82xpls89HU5zKeVoyR8XzHd1RGNOlQlvUe3CFkjWNQ==", + "dev": true + }, + "@types/filesystem": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.32.tgz", + "integrity": "sha512-Yuf4jR5YYMR2DVgwuCiP11s0xuVRyPKmz8vo6HBY3CGdeMj8af93CFZX+T82+VD1+UqHOxTq31lO7MI7lepBtQ==", + "dev": true, + "requires": { + "@types/filewriter": "*" + } + }, + "@types/filewriter": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.29.tgz", + "integrity": "sha512-BsPXH/irW0ht0Ji6iw/jJaK8Lj3FJemon2gvEqHKpCdDCeemHa+rI3WBGq5z7cDMZgoLjY40oninGxqk+8NzNQ==", + "dev": true + }, + "@types/har-format": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.8.tgz", + "integrity": "sha512-OP6L9VuZNdskgNN3zFQQ54ceYD8OLq5IbqO4VK91ORLfOm7WdT/CiT/pHEBSQEqCInJ2y3O6iCm/zGtPElpgJQ==", + "dev": true + }, + "@types/jquery": { + "version": "3.5.14", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.14.tgz", + "integrity": "sha512-X1gtMRMbziVQkErhTQmSe2jFwwENA/Zr+PprCkF63vFq+Yt5PZ4AlKqgmeNlwgn7dhsXEK888eIW2520EpC+xg==", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/node": { + "version": "18.6.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.6.3.tgz", + "integrity": "sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg==", + "dev": true + }, + "@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "@types/trusted-types": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.2.tgz", + "integrity": "sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==", + "dev": true + }, + "@webassemblyjs/ast": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.11.1.tgz", + "integrity": "sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.1.tgz", + "integrity": "sha512-iGRfyc5Bq+NnNuX8b5hwBrRjzf0ocrJPI6GWFodBFzmFnyvrQ83SHKhmilCU/8Jv67i4GJZBMhEzltxzcNagtQ==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.1.tgz", + "integrity": "sha512-RlhS8CBCXfRUR/cwo2ho9bkheSXG0+NwooXcc3PAILALf2QLdFyj7KGsKRbVc95hZnhnERon4kW/D3SZpp6Tcg==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.1.tgz", + "integrity": "sha512-gwikF65aDNeeXa8JxXa2BAk+REjSyhrNC9ZwdT0f8jc4dQQeDQ7G4m0f2QCLPJiMTTO6wfDmRmj/pW0PsUvIcA==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.1.tgz", + "integrity": "sha512-vDkbxiB8zfnPdNK9Rajcey5C0w+QJugEglN0of+kmO8l7lDb77AnlKYQF7aarZuCrv+l0UvqL+68gSDr3k9LPQ==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.1.tgz", + "integrity": "sha512-PvpoOGiJwXeTrSf/qfudJhwlvDQxFgelbMqtq52WWiXC6Xgg1IREdngmPN3bs4RoO83PnL/nFrxucXj1+BX62Q==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.1.tgz", + "integrity": "sha512-10P9No29rYX1j7F3EVPX3JvGPQPae+AomuSTPiF9eBQeChHI6iqjMIwR9JmOJXwpnn/oVGDk7I5IlskuMwU/pg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.1.tgz", + "integrity": "sha512-hJ87QIPtAMKbFq6CGTkZYJivEwZDbQUgYd3qKSadTNOhVY7p+gfP6Sr0lLRVTaG1JjFj+r3YchoqRYxNH3M0GQ==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.1.tgz", + "integrity": "sha512-BJ2P0hNZ0u+Th1YZXJpzW6miwqQUGcIHT1G/sf72gLVD9DZ5AdYTqPNbHZh6K1M5VmKvFXwGSWZADz+qBWxeRw==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.1.tgz", + "integrity": "sha512-9kqcxAEdMhiwQkHpkNiorZzqpGrodQQ2IGrHHxCy+Ozng0ofyMA0lTqiLkVs1uzTRejX+/O0EOT7KxqVPuXosQ==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.1.tgz", + "integrity": "sha512-g+RsupUC1aTHfR8CDgnsVRVZFJqdkFHpsHMfJuWQzWU3tvnLC07UqHICfP+4XyL2tnr1amvl1Sdp06TnYCmVkA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/helper-wasm-section": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-opt": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "@webassemblyjs/wast-printer": "1.11.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.1.tgz", + "integrity": "sha512-F7QqKXwwNlMmsulj6+O7r4mmtAlCWfO/0HdgOxSklZfQcDu0TpLiD1mRt/zF25Bk59FIjEuGAIyn5ei4yMfLhA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.1.tgz", + "integrity": "sha512-VqnkNqnZlU5EB64pp1l7hdm3hmQw7Vgqa0KF/KCNO9sIpI6Fk6brDEiX+iCOYrvMuBWDws0NkTOxYEb85XQHHw==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-buffer": "1.11.1", + "@webassemblyjs/wasm-gen": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.1.tgz", + "integrity": "sha512-rrBujw+dJu32gYB7/Lup6UhdkPx9S9SnobZzRVL7VcBH9Bt9bCBLEuX/YXOOtBsOZ4NQrRykKhffRWHvigQvOA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/helper-api-error": "1.11.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.1", + "@webassemblyjs/ieee754": "1.11.1", + "@webassemblyjs/leb128": "1.11.1", + "@webassemblyjs/utf8": "1.11.1" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.11.1.tgz", + "integrity": "sha512-IQboUWM4eKzWW+N/jij2sRatKMh99QEelo3Eb2q0qXkvPRISAj8Qxtmw5itwqK+TTkBuUIE45AxYPToqPtL5gg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.11.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-import-assertions": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "dev": true + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + } + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "requires": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "caniuse-lite": { + "version": "1.0.30001373", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001373.tgz", + "integrity": "sha512-pJYArGHrPp3TUqQzFYRmP/lwJlj8RCbVe3Gd3eJQkAV8SAC6b19XS9BjMvRdvaS8RMkaTN8ZhoHP6S1y8zzwEQ==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "clean-css": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.1.tgz", + "integrity": "sha512-lCr8OHhiWCTw4v8POJovCoh4T7I9U11yVsPjMWWnnMmp9ZowCxyad1Pathle/9HjaDp+fdQKjO9fQydE6RHTZg==", + "dev": true, + "requires": { + "source-map": "~0.6.0" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "postcss-modules-extract-imports": "^3.0.0", + "postcss-modules-local-by-default": "^4.0.0", + "postcss-modules-scope": "^3.0.0", + "postcss-modules-values": "^4.0.0", + "postcss-value-parser": "^4.2.0", + "semver": "^7.3.5" + } + }, + "cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true + }, + "decorator-cache-getter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/decorator-cache-getter/-/decorator-cache-getter-1.0.0.tgz", + "integrity": "sha512-yB49c5qi0govRjpe18vutEkkKzosIt2XggYSs1Qyev1TJYTh2WmLgDp0dV6VJ/6yNBRlKG+qMG80Vy4Bg0mLJw==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "electron-to-chromium": { + "version": "1.4.210", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.210.tgz", + "integrity": "sha512-kSiX4tuyZijV7Cz0MWVmGT8K2siqaOA4Z66K5dCttPPRh0HicOcOAEj1KlC8O8J1aOS/1M8rGofOzksLKaHWcQ==", + "dev": true + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.3.1.tgz", + "integrity": "sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "es-module-lexer": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz", + "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.11.tgz", + "integrity": "sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "filtrex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/filtrex/-/filtrex-3.0.0.tgz", + "integrity": "sha512-7PUty9Bq+shxb6r1QVo0AmwJVY59HNN76YUD5TIkjtyG6nNhWPTe6dJVq4TKaGdl/V329ayG6rHK1sA31Hmz8Q==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "html-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/html-loader/-/html-loader-4.1.0.tgz", + "integrity": "sha512-QDDNmLgn96NWtTPx/VXRerFXH0hn7cm4bruqsZ333GCb+rqiqGurcxtP/M52wcui1/iLiu0l5ms/McE7/Ik6aQ==", + "dev": true, + "requires": { + "html-minifier-terser": "^6.1.0", + "parse5": "^7.0.0" + } + }, + "html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "requires": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "dependencies": { + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true + } + } + }, + "icss-utils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "dev": true + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "ignore-loader": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ignore-loader/-/ignore-loader-0.1.2.tgz", + "integrity": "sha512-yOJQEKrNwoYqrWLS4DcnzM7SEQhRKis5mB+LdKKh4cPmGYlLPR0ozRzHV5jmEk2IxptqJNQA5Cc0gw8Fj12bXA==", + "dev": true + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "is-core-module": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.9.0.tgz", + "integrity": "sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "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, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "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 + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", + "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "dev": true + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "klona": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.5.tgz", + "integrity": "sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ==", + "dev": true + }, + "lit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-2.3.0.tgz", + "integrity": "sha512-ynSGsUYKSGN2weFQ1F3SZq0Ihlj+vr/3KAET//Yf8Tz86L7lZizlw9Px+ab5iN8Si4RkVoLqd9YtKQmjdyKHNg==", + "dev": true, + "requires": { + "@lit/reactive-element": "^1.4.0", + "lit-element": "^3.2.0", + "lit-html": "^2.3.0" + } + }, + "lit-element": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-3.2.2.tgz", + "integrity": "sha512-6ZgxBR9KNroqKb6+htkyBwD90XGRiqKDHVrW/Eh0EZ+l+iC+u+v+w3/BA5NGi4nizAVHGYvQBHUDuSmLjPp7NQ==", + "dev": true, + "requires": { + "@lit/reactive-element": "^1.3.0", + "lit-html": "^2.2.0" + } + }, + "lit-html": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-2.3.1.tgz", + "integrity": "sha512-FyKH6LTW6aBdkfNhNSHyZTnLgJSTe5hMk7HFtc/+DcN1w74C215q8B+Cfxc2OuIEpBNcEKxgF64qL8as30FDHA==", + "dev": true, + "requires": { + "@types/trusted-types": "^2.0.2" + } + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", + "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash-decorators": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/lodash-decorators/-/lodash-decorators-6.0.1.tgz", + "integrity": "sha512-1M0YC8G3nFTkejZEk2ehyvryEdcqj6xATH+ybI8j53cLs/bKRsavaE//y7nz/A0vxEFhxYqev7vdWfsuTJ1AtQ==", + "dev": true, + "requires": { + "tslib": "^1.9.2" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + } + } + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "requires": { + "tslib": "^2.0.3" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "requires": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "parse5": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.0.0.tgz", + "integrity": "sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g==", + "dev": true, + "requires": { + "entities": "^4.3.0" + } + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "postcss": { + "version": "8.4.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.14.tgz", + "integrity": "sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-modules-extract-imports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "dev": true + }, + "postcss-modules-local-by-default": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.0.tgz", + "integrity": "sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0", + "postcss-selector-parser": "^6.0.2", + "postcss-value-parser": "^4.1.0" + } + }, + "postcss-modules-scope": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz", + "integrity": "sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-modules-values": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", + "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", + "dev": true, + "requires": { + "icss-utils": "^5.0.0" + } + }, + "postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "requires": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + } + }, + "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 + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true + }, + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "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 + }, + "sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "schema-utils": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.1.1.tgz", + "integrity": "sha512-Y5PQxS4ITlC+EahLuXaY86TXfR7Dc5lw294alXOq86JAHCihAIZfqv8nNCWvaEJvaC51uN9hbLGeV0cFBdH+Fw==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "terser": { + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + }, + "terser-webpack-plugin": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.3.tgz", + "integrity": "sha512-Fx60G5HNYknNTNQnzQ1VePRuu89ZVYWfjRAeT5rITuCY/1b08s49e5kSQwHDirKZWuoKOBRFS98EUUoZ9kLEwQ==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.7", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.0", + "terser": "^5.7.2" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "ts-loader": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.3.1.tgz", + "integrity": "sha512-OkyShkcZTsTwyS3Kt7a4rsT/t2qvEVQuKCTg4LJmpj9fhFR7ukGdZwV6Qq3tRUkqcXtfGpPR7+hFKHCG/0d3Lw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "typescript": { + "version": "4.7.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", + "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz", + "integrity": "sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q==", + "dev": true, + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "watchpack": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^0.0.51", + "@webassemblyjs/ast": "1.11.1", + "@webassemblyjs/wasm-edit": "1.11.1", + "@webassemblyjs/wasm-parser": "1.11.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "es-module-lexer": "^0.9.0", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.9", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.1.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.1.3", + "watchpack": "^2.4.0", + "webpack-sources": "^3.2.3" + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..df85c377 --- /dev/null +++ b/package.json @@ -0,0 +1,46 @@ +{ + "name": "csgofloat-extension", + "version": "3.0.0", + "description": "Dedicated API for fetching the float value, paint seed, and screenshots of CS:GO items on the Steam Market or Inventories", + "main": ".eslintrc.js", + "directories": { + "lib": "src/lib" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/csgofloat/extension.git" + }, + "scripts": { + "build": "webpack --env mode=prod --config webpack.config.js --stats-error-details", + "start": "webpack --env mode=development --config webpack.config.js --stats-error-details --watch" + }, + "author": "step7750", + "license": "MIT", + "bugs": { + "url": "https://github.com/csgofloat/extension/issues" + }, + "homepage": "https://github.com/csgofloat/extension#readme", + "devDependencies": { + "@types/chrome": "^0.0.193", + "@types/jquery": "^3.5.14", + "copy-webpack-plugin": "^11.0.0", + "css-loader": "^6.7.1", + "decorator-cache-getter": "^1.0.0", + "file-loader": "^6.2.0", + "glob": "^8.0.3", + "html-loader": "^4.1.0", + "ignore-loader": "^0.1.2", + "lit": "^2.3.0", + "lit-html": "^2.3.1", + "mini-css-extract-plugin": "^2.6.1", + "sass-loader": "^13.0.2", + "ts-loader": "^9.3.1", + "typescript": "^4.7.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0", + "filtrex": "^3.0.0", + "rxjs": "^7.5.7", + "lodash-decorators": "^6.0.1", + "lodash": "^4.17.21" + } +} diff --git a/src/README.md b/src/README.md new file mode 100644 index 00000000..ad6500f5 --- /dev/null +++ b/src/README.md @@ -0,0 +1,157 @@ +# Developer Overview + +## Execution + +### Background + +Typical Chrome extensions deal with 3 different JavaScript execution environments: + +1. Content Scripts + * Access to the page DOM, has its own isolated JavaScript runtime + * AJAX requests may be _limited_ by the security policy of the Steam page + * Can access [some](https://developer.chrome.com/docs/extensions/mv3/content_scripts/#capabilities) Chrome APIs +2. Background Scripts ([Service Workers in MV3](https://developer.chrome.com/docs/extensions/mv3/service_workers/)) + * Background-running JavaScript runtime that is an event handler running _most of the time_ with consistent state + * Notably, AJAX requests are _free_ of page restrictions and CSP +3. Page + * JavaScript runtime of the page itself (in our case, the Steam page) + * Has definitions for all the functions/properties/etc... that Valve defined + +Both 1) and 2) **DON'T** have access to the JS runtime of the page itself (ie. the Steam page). + +Coincidentally, having access to the page's runtime environment is _very useful_, allowing us to easily change state, +hook functions, and access global variables that contain assets. + +Of note, any logic for the extension is typically done within a content script, since the notion of a "page script" +is not _as_ natively supported (ie. Chrome injects a script into the page for you from the manifest). + +### Typical Solutions + +#### Accessing the Page JS Runtime from Content Scripts + +Typically, only being able to access the DOM (ie. HTML env) of the page is not enough or more cumbersome to make +changes to the page for your extension. + +Historically, many extensions would use on-demand script injection into the page in order to retrieve a variable, +call a function, or mutate page-JS state. This is what our extension [used to do](https://github.com/csgofloat/extension/blob/ca85d56e3b268330537daf6bc6be7837213cc7a4/lib/bridge.js) +and what others like CSGO-Trader [currently do](https://github.com/gergelyszabo94/csgo-trader-extension/blob/216df0e4eb6c481c893426d2324b93da026e92d3/extension/src/utils/injection.js#L4) (as of 2022/10/01). + +Pros + * Straightforward to implement + +Cons + * Difficult to implement type checking + * Difficult to decipher which runtime a piece of code is suitable to run in + * Complicated runtimes and state juggling around where to get variables + * Performance bottlenecks (creates a new script node in the DOM _every time you want a variable_) + +#### Making AJAX requests in the Background Script + +Since Steam's Content Security Policy restrictions are applied to the content script's AJAX requests, typically event messaging to the background +script is done. The background script is not restricted and will perform the request for us and send back the result. + +This is the mechanism you'd need to use whenever you fetch an HTTPS resource (like `https://api.csgofloat.com`). + +A naiive example would be: + +`content_script.js` +```javascript +chrome.runtime.sendMessage({type1: 'https://api.csgofloat.com/?url=steam://....'}, (response) => { + // do something +}); +``` + +`background_script.js` +```javascript +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + if (request.type1) { + fetch(request.type1).then(resp => sendResponse(resp)); + } else if (request.type2) { + fetch(request.type2).then(resp => sendResponse(resp)); + } + ... +} +``` + +Pros +* One of the few ways to bypass CSP for requests (without making more destructive changes) + +Cons +* Your background script typically gets _very messy_ with handling logic for every type of request your extension makes +* Type-checking is _hard_, easy to lose context of what you expect a request to return + + +### How CSGOFloat's Extension Works + +#### Accessing the Page's JS runtime + +Almost the entirety of CSGOFloat's Extension runs within the page context and **not** the content script. + +This allows us to easily access page globals, call Steam's functions, override their functions. Additionally, this gives a clear +consistent environment to think about as a developer. + +Since there isn't a native way to tell Chrome via. the manifest to inject a script into the page, content scripts are instead used to **bootstrap** the page +script. This effectively tells Chrome to re-run the script, but in the page instead. Page scripts can be found in `/page_scripts`. + + +#### Making AJAX requests in the Page Context + +Now that our scripts run within the page, we still want to be able to make AJAX requests to other domains outside of +Steam's Content Security Policy. + +CSGOFloat's Extension similarly uses the mechanism of making the actual request in a background script, but +creates an abstraction layer on top. + +Dubbed the "bridge", it allows for **type safe** request and response handling between the page and background script. +You can think of the background script operating as a server that receives incoming HTTP requests and handles +them, sending the response back to the client. + +Typically, any form of foreign HTTP requests or accessing the extension's APIs is done through this bridge. + +You can find more details in `/bridge`. + +## DOM Manipulation + +### Component Creation + +When try to mutate a page, you also want the ability to create new UI components, potentially _reusing_ the styling +on the page. For example, you'd create a component that shows the float for a given item. + +CSGOFloat's Extension uses Web Components via the library [Lit](https://lit.dev/). Each UI mutation is a separate +component that has its own state management and rendering logic. + +You can find our components in `/components`. + + +### Component Injection + +While in the typical world, component creation allows you to composite the page without any more hassle, in an extension +it is _very_ common that you'd want to _augment_ an existing HTML element. + +Our system provides a declarative syntax that tells the library where you want your UI component to be injected into. + +For instance, you may want to augment a market listing row and add the float/seed to it. + +You could do: + +```javascript +@CustomElement() +@InjectAppend("#searchResultsRows .market_listing_row", InjectionMode.CONTINUOUS) +export class HelloWorld extends FloatElement { + ... + render() { + return html`Hello World`; + } +} +``` + +This would then add your `Hello World` component to the page automatically for each listing row. If new listings are +added (ie. they go to the next page), `InjectionMode.CONTINUOUS` will inject into the new rows as well. + + +### Component Scope + +Components are created in the [shadow dom](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM) +and _do not_ have access to the styles that already exist on the page. + +There are global styles inherited by `FloatElement`, but generally styling is done on a per-component basis. diff --git a/src/background.ts b/src/background.ts new file mode 100644 index 00000000..cacd61f2 --- /dev/null +++ b/src/background.ts @@ -0,0 +1,29 @@ +import {Handle} from "./lib/bridge/server"; +import {InternalResponseBundle} from "./lib/bridge/types"; +import MessageSender = chrome.runtime.MessageSender; + +function unifiedHandler(request: any, sender: MessageSender, sendResponse: (response?: any) => void) { + Handle(request, sender).then(response => { + sendResponse({ + request_type: request.request_type, + id: request.id, + response + } as InternalResponseBundle); + }).catch((error) => { + sendResponse({ + request_type: request.request_type, + id: request.id, + error: error.toString() + } as InternalResponseBundle); + }); +} + +chrome.runtime.onMessage.addListener((request, sender, sendResponse) => { + unifiedHandler(request, sender, sendResponse); + return true; +}); + +chrome.runtime.onMessageExternal.addListener((request, sender, sendResponse) => { + unifiedHandler(request, sender, sendResponse); + return true; +}); diff --git a/src/global.css b/src/global.css new file mode 100644 index 00000000..96996b65 --- /dev/null +++ b/src/global.css @@ -0,0 +1,64 @@ +.csgofloat-stickers-container { + float: right; +} + +.csgofloat-easy-inspect { + position: absolute; + height: 74px; + width: 74px; + left: 0; + top: 0; + vertical-align: middle; + text-align: center; + line-height: 74px; + display: none; + font-size: 20px; +} + +.market_recent_listing_row:hover .csgofloat-easy-inspect { + display: initial; +} + +/* Powers animation for shining float boxes */ +.csgofloat-shine { + overflow: hidden; +} + +/* Based on https://jsfiddle.net/AntonTrollback/nqQc7/ */ +.csgofloat-shine:after { + animation: csgofloat-shine-frames 4s ease-in-out infinite; + content: ""; + position: absolute; + top: -110%; + left: -210%; + width: 200%; + height: 200%; + opacity: 0; + transform: rotate(30deg); + + background: rgba(255, 255, 255, 0.13); + background: linear-gradient( + to right, + rgba(255, 255, 255, 0) 0%, + rgba(255, 255, 255, 0.13) 77%, + rgba(255, 255, 255, 0.5) 92%, + rgba(255, 255, 255, 0) 100% + ); +} + +@keyframes csgofloat-shine-frames { + 50% { + opacity: 1; + top: -30%; + left: -30%; + transition-property: left, top, opacity; + transition-duration: 0.7s, 0.7s, 0.15s; + transition-timing-function: ease; + } + 100% { + opacity: 0; + top: -30%; + left: -30%; + transition-property: left, top, opacity; + } +} diff --git a/src/lib/bridge/client.ts b/src/lib/bridge/client.ts new file mode 100644 index 00000000..4b4d6765 --- /dev/null +++ b/src/lib/bridge/client.ts @@ -0,0 +1,20 @@ +import {InternalRequestBundle, InternalResponseBundle, RequestHandler, Version} from "./types"; + +export async function ClientSend(handler: RequestHandler, args: Req): Promise { + const bundle: InternalRequestBundle = { + version: Version.V1, + request_type: handler.getType(), + request: args, + id: Math.ceil(Math.random() * 100000000000) + }; + + return new Promise((resolve, reject) => { + chrome.runtime.sendMessage(window.CSGOFLOAT_EXTENSION_ID || chrome.runtime.id, bundle, (resp: InternalResponseBundle) => { + if (resp?.response) { + resolve(resp.response); + } else { + reject(resp?.error); + } + }); + }); +} diff --git a/src/lib/bridge/handlers/csmoney_price.ts b/src/lib/bridge/handlers/csmoney_price.ts new file mode 100644 index 00000000..21519816 --- /dev/null +++ b/src/lib/bridge/handlers/csmoney_price.ts @@ -0,0 +1,33 @@ +import {RequestType, SimpleHandler} from "./main"; + +export interface CSMoneyPriceRequest { + marketHashName: string; +} + +interface DynamicBanner { + dynamic: boolean; + link: string; + src: string; + height: string; +} + +interface ImageBanner { + link: string; + src: string; + height: string; +} + +interface Banner extends DynamicBanner, ImageBanner {} + +export interface CSMoneyPriceResponse { + price: number; + banner?: {enable?: boolean} & Banner; +} + +export const CSMoneyPrice = new SimpleHandler( + RequestType.CSMONEY_PRICE, + async (req, sender) => { + return fetch(`https://money.csgofloat.com/price?name=${req.marketHashName}`).then(resp => { + return resp.json() as Promise; + }); + }); diff --git a/src/lib/bridge/handlers/execute_css.ts b/src/lib/bridge/handlers/execute_css.ts new file mode 100644 index 00000000..580f8a13 --- /dev/null +++ b/src/lib/bridge/handlers/execute_css.ts @@ -0,0 +1,15 @@ +import {EmptyResponseHandler, RequestType, PrivilegedHandler} from "./main"; + +interface ExecuteCssRequest { + path: string; +} + +export const ExecuteCssOnPage = new PrivilegedHandler(new EmptyResponseHandler( + RequestType.EXECUTE_CSS_ON_PAGE, + async (req, sender) => { + await chrome.scripting.insertCSS( + { + target: {tabId: sender.tab?.id as number}, + files: [req.path], + }); + })); diff --git a/src/lib/bridge/handlers/execute_script.ts b/src/lib/bridge/handlers/execute_script.ts new file mode 100644 index 00000000..66fd1e9b --- /dev/null +++ b/src/lib/bridge/handlers/execute_script.ts @@ -0,0 +1,31 @@ +import {EmptyResponseHandler, RequestType, PrivilegedHandler} from "./main"; + +interface ExecuteScriptRequest { + path: string; +} + +export const ExecuteScriptOnPage = new PrivilegedHandler(new EmptyResponseHandler( + RequestType.EXECUTE_SCRIPT_ON_PAGE, + async (req, sender) => { + // We need to inject the extension ID dynamically so the client knows who to + // communicate with. + // + // On Firefox, extension IDs are random, so this is necessary. + await chrome.scripting.executeScript( + { + target: {tabId: sender.tab?.id as number}, + world: 'MAIN', + args: [chrome.runtime.id, chrome.runtime.getURL('src/model_frame.html')], + func: function ExtensionId(extensionId, modelFrameUrl) { + window.CSGOFLOAT_EXTENSION_ID = extensionId; + window.CSGOFLOAT_MODEL_FRAME_URL = modelFrameUrl; + } + }); + + await chrome.scripting.executeScript( + { + target: {tabId: sender.tab?.id as number}, + files: [req.path], + world: 'MAIN' + }); + })); diff --git a/src/lib/bridge/handlers/fetch_inspect_info.ts b/src/lib/bridge/handlers/fetch_inspect_info.ts new file mode 100644 index 00000000..597b8aac --- /dev/null +++ b/src/lib/bridge/handlers/fetch_inspect_info.ts @@ -0,0 +1,64 @@ +import {RequestType, SimpleHandler} from "./main"; + +interface Sticker { + slot: number; + stickerId: number; + codename?: string; + material?: string; + name?: string; + wear?: number; +} + +export interface ItemInfo { + stickers: Sticker[]; + itemid: string; + defindex: number; + paintindex: number; + rarity: number; + quality: number; + paintseed: number; + inventory: number; + origin: number; + s: string; + a: string; + d: string; + m: string; + floatvalue: number; + imageurl: string; + min: number; + max: number; + weapon_type?: string; + item_name?: string; + rarity_name?: string; + quality_name?: string; + origin_name?: string; + wear_name?: string; + full_item_name?: string; + low_rank?: number; + high_rank?: number; +} + +export interface FetchInspectInfoRequest { + link: string; + listPrice?: number; +} + +export interface FetchInspectInfoResponse { + iteminfo: ItemInfo; + error?: string; +} + +export const FetchInspectInfo = new SimpleHandler( + RequestType.FETCH_INSPECT_INFO, + (req) => { + const apiUrl = `https://api.csgofloat.com/?url=${req.link}&minimal=true${req.listPrice ? '&listPrice=' + req.listPrice : ''}`; + return fetch(apiUrl).then(resp => { + return resp.json().then((json: FetchInspectInfoResponse) => { + if (resp.ok) { + return json; + } else { + throw Error(json.error); + } + }) as Promise; + }); + }); diff --git a/src/lib/bridge/handlers/fetch_pending_trades.ts b/src/lib/bridge/handlers/fetch_pending_trades.ts new file mode 100644 index 00000000..6a27ff69 --- /dev/null +++ b/src/lib/bridge/handlers/fetch_pending_trades.ts @@ -0,0 +1,17 @@ +import {RequestType, SimpleHandler} from "./main"; +import {Trade} from "../../types/float_market"; + +export interface FetchPendingTradesRequest {} + +export interface FetchPendingTradesResponse { + trades_to_send: Trade[]; + trades_to_receive: Trade[]; +} + +export const FetchPendingTrades = new SimpleHandler( + RequestType.FETCH_PENDING_TRADES, + async (req) => { + return fetch(`https://csgofloat.com/api/v1/me/pending-trades`, {credentials: 'include'}).then(resp => { + return resp.json() as Promise; + }); + }); diff --git a/src/lib/bridge/handlers/fetch_skin_model.ts b/src/lib/bridge/handlers/fetch_skin_model.ts new file mode 100644 index 00000000..467ac60c --- /dev/null +++ b/src/lib/bridge/handlers/fetch_skin_model.ts @@ -0,0 +1,26 @@ +import {RequestType, SimpleHandler} from "./main"; + +export interface FetchSkinModelRequest { + inspectLink: string; +} + +export interface FetchSkinModelResponse { + modelLink: string; + screenshotLink: string; + + error?: string; +} + +export const FetchSkinModel = new SimpleHandler( + RequestType.FETCH_SKIN_MODEL, + async (req) => { + return fetch(`https://money.csgofloat.com/model?url=${req.inspectLink}`).then(resp => { + return resp.json().then(data => { + if (resp.ok) { + return data; + } else { + throw new Error(data.error); + } + }) as Promise; + }); + }); diff --git a/src/lib/bridge/handlers/fetch_stall.ts b/src/lib/bridge/handlers/fetch_stall.ts new file mode 100644 index 00000000..e6d16b1c --- /dev/null +++ b/src/lib/bridge/handlers/fetch_stall.ts @@ -0,0 +1,30 @@ +import {RequestType, SimpleHandler} from "./main"; +import {Contract, User} from "../../types/float_market"; + +export interface FetchStallRequest { + steam_id64: string; +} + +export interface FetchStallResponse { + listings?: Contract[]; + user?: User; +} + +export interface FetchStallResponseError { + code?: string; + message?: string; +} + +export const FetchStall = new SimpleHandler( + RequestType.FETCH_STALL, + async (req) => { + return fetch(`https://csgofloat.com/api/v1/users/${req.steam_id64}/stall`).then(resp => { + return resp.json().then((json: FetchStallResponse|FetchStallResponseError) => { + if (resp.ok) { + return json; + } else { + throw Error((json as FetchStallResponseError).message); + } + }) as Promise; + }); + }); diff --git a/src/lib/bridge/handlers/handlers.ts b/src/lib/bridge/handlers/handlers.ts new file mode 100644 index 00000000..1971bb28 --- /dev/null +++ b/src/lib/bridge/handlers/handlers.ts @@ -0,0 +1,25 @@ +import {ExecuteScriptOnPage} from "./execute_script"; +import {FetchStall} from "./fetch_stall"; +import {FetchInspectInfo} from "./fetch_inspect_info"; +import {ExecuteCssOnPage} from "./execute_css"; +import {StorageGet} from "./storage_get"; +import {StorageSet} from "./storage_set"; +import {CSMoneyPrice} from "./csmoney_price"; +import {RequestType} from "./main"; +import {RequestHandler} from "../types"; +import {FetchPendingTrades} from "./fetch_pending_trades"; +import {FetchSkinModel} from "./fetch_skin_model"; +import {StorageRemove} from "./storage_remove"; + +export const HANDLERS_MAP: {[key in RequestType]: RequestHandler} = { + [RequestType.EXECUTE_SCRIPT_ON_PAGE]: ExecuteScriptOnPage, + [RequestType.EXECUTE_CSS_ON_PAGE]: ExecuteCssOnPage, + [RequestType.FETCH_INSPECT_INFO]: FetchInspectInfo, + [RequestType.FETCH_STALL]: FetchStall, + [RequestType.STORAGE_GET]: StorageGet, + [RequestType.STORAGE_SET]: StorageSet, + [RequestType.STORAGE_REMOVE]: StorageRemove, + [RequestType.CSMONEY_PRICE]: CSMoneyPrice, + [RequestType.FETCH_PENDING_TRADES]: FetchPendingTrades, + [RequestType.FETCH_SKIN_MODEL]: FetchSkinModel, +} diff --git a/src/lib/bridge/handlers/main.ts b/src/lib/bridge/handlers/main.ts new file mode 100644 index 00000000..c1fc3363 --- /dev/null +++ b/src/lib/bridge/handlers/main.ts @@ -0,0 +1,73 @@ +import {RequestHandler} from "../types"; +import MessageSender = chrome.runtime.MessageSender; + +export enum RequestType { + EXECUTE_SCRIPT_ON_PAGE, + EXECUTE_CSS_ON_PAGE, + FETCH_INSPECT_INFO, + FETCH_STALL, + STORAGE_GET, + STORAGE_SET, + STORAGE_REMOVE, + CSMONEY_PRICE, + FETCH_PENDING_TRADES, + FETCH_SKIN_MODEL, +} + +export class SimpleHandler implements RequestHandler { + constructor(private type: RequestType, private handler: (request: Req, sender: MessageSender) => Promise) {} + + getType(): RequestType { + return this.type; + } + + handleRequest(request: Req, sender: MessageSender): Promise { + return this.handler(request, sender); + } +} + +export interface Empty {} + +export class EmptyRequestHandler implements RequestHandler { + constructor(private type: RequestType, private handler: (sender: MessageSender) => Promise) {} + + getType(): RequestType { + return this.type; + } + + handleRequest(request: Empty, sender: MessageSender): Promise { + return this.handler(sender); + } +} + +export class EmptyResponseHandler implements RequestHandler { + constructor(private type: RequestType, private handler: (request: Req, sender: MessageSender) => Promise) {} + + getType(): RequestType { + return this.type; + } + + handleRequest(request: Req, sender: MessageSender): Promise { + return this.handler(request, sender); + } +} + +/** + * Restricts a given handler such that it can only run if the sender is + * verified to be from the extension's origin (ie. content script) + */ +export class PrivilegedHandler implements RequestHandler { + constructor(private handler: RequestHandler) {} + + getType(): RequestType { + return this.handler.getType(); + } + + handleRequest(request: Req, sender: MessageSender): Promise { + if (sender.id !== chrome.runtime.id) { + throw new Error('Attempt to access restricted method outside of secure context (ie. content script)'); + } + + return this.handler.handleRequest(request, sender); + } +} diff --git a/src/lib/bridge/handlers/storage_get.ts b/src/lib/bridge/handlers/storage_get.ts new file mode 100644 index 00000000..65886eb3 --- /dev/null +++ b/src/lib/bridge/handlers/storage_get.ts @@ -0,0 +1,31 @@ +import {RequestType} from "./main"; +import {RequestHandler} from "../types"; +import {gStore} from "../../storage/store"; +import {ClientSend} from "../client"; +import {DynamicStorageKey, StorageKey, StorageRow} from "../../storage/keys"; + +interface StorageGetRequest { + key: StorageKey|DynamicStorageKey; +} + +interface StorageGetResponse { + value: T|null; +} + +class StorageGetHandler implements RequestHandler> { + getType(): RequestType { + return RequestType.STORAGE_GET; + } + + async handleRequest(request: StorageGetRequest, sender: chrome.runtime.MessageSender): Promise> { + const value = await gStore.get(request.key); + return {value}; + } +} + +export async function Get(row: StorageRow): Promise { + const resp = await ClientSend(new StorageGetHandler(), {key: row.key}); + return resp.value; +} + +export const StorageGet = new StorageGetHandler(); diff --git a/src/lib/bridge/handlers/storage_remove.ts b/src/lib/bridge/handlers/storage_remove.ts new file mode 100644 index 00000000..f9f8f2cf --- /dev/null +++ b/src/lib/bridge/handlers/storage_remove.ts @@ -0,0 +1,28 @@ +import {RequestType} from "./main"; +import {RequestHandler} from "../types"; +import {gStore} from "../../storage/store"; +import {ClientSend} from "../client"; +import {DynamicStorageKey, StorageKey, StorageRow} from "../../storage/keys"; + +interface StorageRemoveRequest { + key: StorageKey|DynamicStorageKey; +} + +interface StorageRemoveResponse {} + +class StorageRemoveHandler implements RequestHandler, StorageRemoveResponse> { + getType(): RequestType { + return RequestType.STORAGE_REMOVE; + } + + async handleRequest(request: StorageRemoveRequest, sender: chrome.runtime.MessageSender): Promise { + await gStore.remove(request.key); + return {} as StorageRemoveResponse; + } +} + +export const StorageRemove = new StorageRemoveHandler(); + +export function Remove(row: StorageRow): Promise { + return ClientSend(StorageRemove, {key: row.key}); +} diff --git a/src/lib/bridge/handlers/storage_set.ts b/src/lib/bridge/handlers/storage_set.ts new file mode 100644 index 00000000..632b1580 --- /dev/null +++ b/src/lib/bridge/handlers/storage_set.ts @@ -0,0 +1,29 @@ +import {RequestType} from "./main"; +import {RequestHandler} from "../types"; +import {gStore} from "../../storage/store"; +import {ClientSend} from "../client"; +import {DynamicStorageKey, StorageKey, StorageRow} from "../../storage/keys"; + +interface StorageSetRequest { + key: StorageKey|DynamicStorageKey; + value: T; +} + +interface StorageSetResponse {} + +class StorageSetHandler implements RequestHandler, StorageSetResponse> { + getType(): RequestType { + return RequestType.STORAGE_SET; + } + + async handleRequest(request: StorageSetRequest, sender: chrome.runtime.MessageSender): Promise { + await gStore.set(request.key, request.value); + return {} as StorageSetResponse; + } +} + +export const StorageSet = new StorageSetHandler(); + +export function Set(row: StorageRow, value: T): Promise { + return ClientSend(StorageSet, {key: row.key, value}); +} diff --git a/src/lib/bridge/server.ts b/src/lib/bridge/server.ts new file mode 100644 index 00000000..636fbf73 --- /dev/null +++ b/src/lib/bridge/server.ts @@ -0,0 +1,24 @@ +import {RequestType} from "./handlers/main"; +import {InternalRequestBundle, RequestHandler, Version} from "./types"; +import MessageSender = chrome.runtime.MessageSender; +import {HANDLERS_MAP} from "./handlers/handlers"; + +function findHandler(type: RequestType): RequestHandler|undefined { + return HANDLERS_MAP[type]; +} + +export async function Handle(blob: any, sender: MessageSender): Promise { + if (blob.version !== Version.V1) { + // Ignore messages that aren't for this bridge + return; + } + + const req: InternalRequestBundle = blob as InternalRequestBundle; + + const handler = findHandler(req.request_type); + if (!handler) { + throw new Error(`couldn't find handler for request type ${req.request_type}`); + } + + return handler.handleRequest(req.request, sender); +} diff --git a/src/lib/bridge/types.ts b/src/lib/bridge/types.ts new file mode 100644 index 00000000..97d6efa5 --- /dev/null +++ b/src/lib/bridge/types.ts @@ -0,0 +1,35 @@ +import {RequestType} from "./handlers/main"; +import MessageSender = chrome.runtime.MessageSender; + +export interface RequestHandler { + handleRequest(request: Req, sender: MessageSender): Promise; + getType(): RequestType; +} + +export enum Version { + V1 = 'CSGOFLOAT_V1' +} + +export interface InternalRequestBundle { + version: string; + + request_type: RequestType; + + // Input request + request: any; + + // Random ID to identify the request + id: number; +} + +export interface InternalResponseBundle { + request_type: RequestType; + + // Response + response: any; + + error: string; + + // Random ID to identify the request + id: number; +} diff --git a/src/lib/components/common/item_holder_metadata.ts b/src/lib/components/common/item_holder_metadata.ts new file mode 100644 index 00000000..7f246721 --- /dev/null +++ b/src/lib/components/common/item_holder_metadata.ts @@ -0,0 +1,113 @@ +import {FloatElement} from "../custom"; +import {html, css, HTMLTemplateResult} from "lit"; +import {state} from "lit/decorators.js"; +import {Asset} from "../../types/steam"; +import {gFloatFetcher} from "../../float_fetcher/float_fetcher"; +import {ItemInfo} from "../../bridge/handlers/fetch_inspect_info"; +import {formatFloatWithRank, formatSeed, getLowestRank} from "../../utils/skin"; +import {isSkin} from "../../utils/skin"; +import {getRankColour} from "../../utils/ranks"; +import {Observe} from "../../utils/observers"; + +// Generic annotator of item holder metadata (float, seed, etc...) +// Must be extended to use as a component +export abstract class ItemHolderMetadata extends FloatElement { + static styles = [...FloatElement.styles, css` + .float { + position: absolute; + bottom: 3px; + right: 3px; + font-size: 12px; + } + + .seed { + position: absolute; + top: 3px; + right: 3px; + font-size: 12px; + } + `]; + + @state() + private itemInfo: ItemInfo|undefined; + + get assetId(): string|undefined { + return $J(this).parent().attr('id')?.split('_')[2]; + } + + abstract get asset(): Asset|undefined; + abstract get ownerSteamId(): string|undefined; + + get inspectLink(): string|undefined { + if (!this.asset) return; + + if (!this.asset?.actions || this.asset?.actions?.length === 0) return; + + if (!this.ownerSteamId) { + return; + } + + return this.asset?.actions![0].link + .replace('%owner_steamid%', this.ownerSteamId) + .replace('%assetid%', this.assetId!); + } + + protected render(): HTMLTemplateResult { + if (!this.itemInfo) return html``; + + return html` + + ${formatFloatWithRank(this.itemInfo, 6)} + ${formatSeed(this.itemInfo)} + + `; + } + + async connectedCallback() { + super.connectedCallback(); + + if (this.inspectLink) { + this.onInit(); + } else { + // Wait until the asset exists + Observe(() => this.inspectLink, () => { + if (this.inspectLink) { + this.onInit(); + } + }, 200); + } + } + + async onInit() { + if (!this.asset) return; + + if (!isSkin(this.asset)) return; + + // Commodities won't have inspect links + if (!this.inspectLink) return; + + try { + this.itemInfo = await gFloatFetcher.fetch({ + link: this.inspectLink + }); + } catch (e: any) { + console.error(`Failed to fetch float for ${this.assetId}: ${e.toString()}`); + } + + if (this.itemInfo) { + this.annotateRankShine(this.itemInfo); + } + } + + annotateRankShine(info: ItemInfo) { + const rank = getLowestRank(info); + if (!rank || rank > 5) { + return; + } + + // Make the inventory box coloured ;) + $J(this).parent().css('color', 'black'); + $J(this).parent().find('img').css('background-color', getRankColour(rank)); + $J(this).parent().addClass('csgofloat-shine'); + } +} diff --git a/src/lib/components/common/ui/steam-button.ts b/src/lib/components/common/ui/steam-button.ts new file mode 100644 index 00000000..81f78e47 --- /dev/null +++ b/src/lib/components/common/ui/steam-button.ts @@ -0,0 +1,125 @@ +import {css, html} from 'lit'; +import { classMap } from 'lit-html/directives/class-map.js'; + +import {property} from 'lit/decorators.js'; +import {CustomElement} from "../../injectors"; +import {FloatElement} from "../../custom"; + + +enum ButtonType { + GreenWhite = 'green_white', + GreyWhite = 'grey_white' +} + +@CustomElement() +export class SteamButton extends FloatElement { + @property({type: String}) + private text: string = ''; + + @property({type: String}) + private type: ButtonType = ButtonType.GreenWhite; + + static styles = [...FloatElement.styles, css` + .btn_green_white_innerfade { + border-radius: 2px; + border: none; + padding: 1px; + display: inline-block; + cursor: pointer; + text-decoration: none !important; + color: #D2E885 !important; + + background: #a4d007; + background: -webkit-linear-gradient(top, #a4d007 5%, #536904 95%); + background: linear-gradient(to bottom, #a4d007 5%, #536904 95%); + } + + .btn_green_white_innerfade > span { + border-radius: 2px; + display: block; + + + background: #799905; + background: -webkit-linear-gradient(top, #799905 5%, #536904 95%); + background: linear-gradient(to bottom, #799905 5%, #536904 95%); + } + + .btn_green_white_innerfade:not(.btn_disabled):not(:disabled):not(.btn_active):not(.active):hover { + text-decoration: none !important; + color: #fff !important; + + background: #b6d908; + background: -webkit-linear-gradient(top, #b6d908 5%, #80a006 95%); + background: linear-gradient(to bottom, #b6d908 5%, #80a006 95%); + } + + .btn_green_white_innerfade:not(.btn_disabled):not(:disabled):not(.btn_active):not(.active):hover > span { + background: #a1bf07; + background: -webkit-linear-gradient(top, #a1bf07 5%, #80a006 95%); + background: linear-gradient(to bottom, #a1bf07 5%, #80a006 95%); + } + + .btn_grey_white_innerfade { + border-radius: 2px; + border: none; + padding: 1px; + display: inline-block; + cursor: pointer; + text-decoration: none !important; + color: #fff !important; + + background: #acb5bd; + background: -webkit-linear-gradient(top, #acb5bd 5%, #414a52 95%); + background: linear-gradient(to bottom, #acb5bd 5%, #414a52 95%); + } + + .btn_grey_white_innerfade > span { + border-radius: 2px; + display: block; + + + background: #778088; + background: -webkit-linear-gradient(top, #778088 5%, #414a52 95%); + background: linear-gradient(to bottom, #778088 5%, #414a52 95%); + } + + .btn_grey_white_innerfade:not(.btn_disabled):not(:disabled):not(.btn_active):not(.active):hover { + text-decoration: none !important; + color: #fff !important; + + background: #cfd8e0; + background: -webkit-linear-gradient(top, #cfd8e0 5%, #565f67 95%); + background: linear-gradient(to bottom, #cfd8e0 5%, #565f67 95%); + } + + .btn_grey_white_innerfade:not(.btn_disabled):not(:disabled):not(.btn_active):not(.active):hover > span { + background: #99a2aa; + background: -webkit-linear-gradient(top, #99a2aa 5%, #565f67 95%); + background: linear-gradient(to bottom, #99a2aa 5%, #565f67 95%); + } + + .btn_small > span { + padding: 0 15px; + font-size: 12px; + line-height: 20px; + } + `]; + + async connectedCallback() { + super.connectedCallback(); + } + + btnClass() { + const r: {[key: string]: boolean} = {'btn_small': true}; + r[`btn_${this.type}_innerfade`] = true; + return classMap(r); + } + + render() { + return html` + + ${this.text} + + `; + } +} diff --git a/src/lib/components/custom.ts b/src/lib/components/custom.ts new file mode 100644 index 00000000..46c8fd4e --- /dev/null +++ b/src/lib/components/custom.ts @@ -0,0 +1,56 @@ +import {css, LitElement} from "lit"; + +function camelToDashCase(str: string) { + return str.split(/(?=[A-Z])/).join('-').toLowerCase(); +} + +// LitElement wrapper with a pre-determined tag +export class FloatElement extends LitElement { + static styles = [css` + hr { + background-color: #1b2939; + border-style: solid none none; + border-color: black; + border-width: 1px 0 0; + height: 2px; + } + + a { + color: #ebebeb; + cursor: pointer; + } + + input[type=text], input[type=password], input[type=number], select { + color: #909090; + background-color: rgba(0, 0, 0, 0.2); + border: 1px solid #000; + border-radius: 3px; + } + + input[type=color] { + float: left; + margin-top: 2px; + -webkit-appearance: none; + border: none; + width: 20px; + height: 20px; + padding: 0; + } + + input[type=color]::-webkit-color-swatch-wrapper { + padding: 0; + } + + input[type=color]::-webkit-color-swatch { + border: none; + } + `]; + + static tag(): string { + return `csgofloat-${camelToDashCase(this.name)}`; + } + + static elem(): any { + return document.createElement(this.tag()); + } +} diff --git a/src/lib/components/filter/filter_container.ts b/src/lib/components/filter/filter_container.ts new file mode 100644 index 00000000..11bd242b --- /dev/null +++ b/src/lib/components/filter/filter_container.ts @@ -0,0 +1,50 @@ +import {html} from 'lit'; + +import {property, state} from 'lit/decorators.js'; +import {CustomElement} from "../injectors"; +import {FloatElement} from "../custom"; +import {Filter} from "../../filter/filter"; +import {DYNAMIC_ITEM_FILTERS} from "../../storage/keys"; +import {gFilterService} from "../../filter/service"; + +import './filter_creator'; +import './filter_view'; + +@CustomElement() +export class FilterContainer extends FloatElement { + @property({type: String}) + private key: string = ''; + + @state() + private filters: Filter[] = []; + + async connectedCallback() { + super.connectedCallback(); + + if (!this.key) { + throw new Error('filter key MUST be defined'); + } + + gFilterService.onUpdate$.subscribe((filters) => { + this.filters = [...filters]; + }); + + await gFilterService.initialize(DYNAMIC_ITEM_FILTERS(this.key)); + } + + render() { + return html` + ${this.filters.map(filter => { + return html`
+ +
+
` + })} + + `; + } + + onNewFilter(e: CustomEvent) { + gFilterService.upsert(e.detail.filter); + } +} diff --git a/src/lib/components/filter/filter_creator.ts b/src/lib/components/filter/filter_creator.ts new file mode 100644 index 00000000..3a92453d --- /dev/null +++ b/src/lib/components/filter/filter_creator.ts @@ -0,0 +1,129 @@ +import {css, html, HTMLTemplateResult, nothing} from 'lit'; +import { styleMap } from 'lit-html/directives/style-map.js'; + +import {state, query} from 'lit/decorators.js'; +import {CustomElement} from "../injectors"; +import {FloatElement} from "../custom"; +import {Filter} from "../../filter/filter"; + +import '../common/ui/steam-button'; +import './filter_help'; +import {debounce} from "lodash-decorators"; + +/** UI for creating a filter */ +@CustomElement() +export class FilterCreator extends FloatElement { + @state() + private error: string = ''; + + @state() + private showHelp = false; + + @query('.expression-input') + private expressionInput!: HTMLInputElement; + + @state() + get expression(): string { + return this.expressionInput?.value; + } + + @query('#colour-input') + private colourInput!: HTMLInputElement; + + get colour(): string { + return this.colourInput?.value; + } + + static styles = [...FloatElement.styles, css` + .expression-input { + width: 350px; + margin-left: 5px; + padding: 4px 4px; + color: #828282; + font-size: 12px; + outline: none; + border: 1px solid #292929; + background-color: #101010; + font-family: "Motiva Sans", Sans-serif, serif; + font-weight: 300; + border-radius: 0; + } + + .help-btn { + font-size: 18px; + margin-left: 5px; + } + + .compile-status { + display: inline; + text-align: left; + margin-left: 5px; + } + + .add-btn { + margin-left: 10px; + } + + .compile-error { + font-family: Consolas, serif; + margin-top: 5px; + } + `]; + + async connectedCallback() { + super.connectedCallback(); + } + + render() { + return html` + + + +
${this.error ? 'X' : '✓'}
+ +
${(this.expression && this.error) || nothing}
+ + `; + } + + // Don't show errors right away as the user is typing + @debounce(500) + onExpressionInput() { + this.requestUpdate(); + if (this.expression === '') return; + + try { + const f = new Filter(this.expression, this.colour, false); + f.validate(); + this.error = ''; + } catch (e: any) { + this.error = e.toString(); + } + } + + reset() { + this.expressionInput!.value = ''; + this.error = ''; + this.requestUpdate(); + } + + onAddFilter() { + this.dispatchEvent(new CustomEvent('newFilter', { + detail: { + filter: new Filter(this.expression, this.colour, false) + } + })); + + this.reset(); + } +} diff --git a/src/lib/components/filter/filter_help.ts b/src/lib/components/filter/filter_help.ts new file mode 100644 index 00000000..10782321 --- /dev/null +++ b/src/lib/components/filter/filter_help.ts @@ -0,0 +1,155 @@ +import {CustomElement} from "../injectors"; +import {FloatElement} from "../custom"; +import {html, HTMLTemplateResult} from "lit"; + +@CustomElement() +export class FilterHelp extends FloatElement { + protected render(): HTMLTemplateResult { + return html` +
+ Filters will highlight matching items with the specified colour

+ + Note: If multiple filters match an item, it will be highlighted with the average colour

+ + New: You can now filter based on FloatDB ranks and item price!

+ + Examples: +
    +
  • float < 0.3
  • +
      +
    • Matches items with floats less than 0.3
    • +
    +
  • float >= 0.112 and float < 0.2
  • +
      +
    • Matches items with floats greater than or equal to 0.112 and less than 0.2
    • +
    +
  • float < 0.02 and price < 12.30
  • +
      +
    • Matches items with floats less than 0.02 and a price of 12.30 or less in your logged-in account currency
    • +
    • Note: Price only works when you're logged in to ensure the proper currency
    • +
    +
  • phase == "Ruby" or phase == "1"
  • +
      +
    • Matches items with a doppler phase 1 or Ruby
    • +
    • Equivalent to phase in ("Ruby", "1")
    • +
    +
  • float == 0.2 or (seed > 500 and float < 0.15)
  • +
      +
    • Matches items with floats of 0.2 or paint seeds greater than 500 and floats less than 0.15
    • +
    +
  • low_rank <= 500
  • +
      +
    • Matches items with floats ranked in the top 500 lowest for this skin on FloatDB
    • +
    +
  • match(float, "7355608") >= 1
  • +
      +
    • Matches items with floats that contain at least one match of the CS bomb code
    • +
    • Example Match: 0.234327355608454
    • +
    +
  • percentile(90)
  • +
      +
    • Matches items with a float better than 90% of items of this type
    • +
    +
  • percentileRange(0, 10)
  • +
      +
    • Matches items with a float within the percentile range 0-10%
    • +
    • This matches the worst 10% of floats of items of this type
    • +
    +
+ + Variables +
    +
  • float
  • +
      +
    • The float value of the item
    • +
    +
  • seed
  • +
      +
    • The paint seed of the item
    • +
    +
  • low_rank
  • +
      +
    • If the item is in the top 1000 lowest float for this skin and category (normal, stattrak, souvenir), this is the FloatDB rank
    • +
    +
  • high_rank
  • +
      +
    • If the item is in the top 1000 highest float for this skin and category (normal, stattrak, souvenir), this is the FloatDB rank
    • +
    +
  • price
  • +
      +
    • Price of the item in your currency in decimal format (ex. 18.43), includes fees
    • +
    • Note: Price only works when you're logged in to ensure the proper currency
    • +
    +
  • phase
  • +
      +
    • Phase of the item if it's a doppler, empty otherwise
    • +
    • Possible values are "1", "2", "3", "4", "Ruby", "Sapphire", "Black Pearl", "Emerald"
    • +
    +
  • minfloat
  • +
      +
    • The minimum float the skin can have (regardless of wear)
    • +
    +
  • maxfloat
  • +
      +
    • The maximum float the skin can have (regardless of wear)
    • +
    +
+ + Functions: +
    +
  • match(x, regex)
  • +
      +
    • Performs a regex match on 'x' and returns the amount of matches
    • +
    +
  • percentile(rank)
  • +
      +
    • Returns true if the skin's float is in the given percentile, lower floats are considered "better"
    • +
    • This takes into account the range of the wear and specific per-skin range
    • +
    • Note: This assumes that floats are distributed evenly
    • +
    +
  • percentileRange(minRank, maxRank)
  • +
      +
    • Returns true if the skin's float is in the given percentile range
    • +
    • This takes into account the range of the wear and specific per-skin range
    • +
    • Note: This assumes that floats are distributed evenly
    • +
    +
  • abs(x)
  • +
      +
    • Absolute value
    • +
    +
  • ceil(x)
  • +
      +
    • Round floating point up
    • +
    +
  • floor(x)
  • +
      +
    • Round floating point down
    • +
    +
  • log(x)
  • +
      +
    • Natural logarithm
    • +
    +
  • max(a, b, c...)
  • +
      +
    • Max value (variable length of args)
    • +
    +
  • min(a, b, c...)
  • +
      +
    • Min value (variable length of args)
    • +
    +
  • random()
  • +
      +
    • Random floating point from 0.0 to 1.0
    • +
    +
  • round(x)
  • +
      +
    • Round floating point
    • +
    +
  • sqrt(x)
  • +
      +
    • Square root
    • +
    +
+ `; + } +} diff --git a/src/lib/components/filter/filter_view.ts b/src/lib/components/filter/filter_view.ts new file mode 100644 index 00000000..57727c7e --- /dev/null +++ b/src/lib/components/filter/filter_view.ts @@ -0,0 +1,66 @@ +import {CustomElement} from "../injectors"; +import {FloatElement} from "../custom"; +import {property} from "lit/decorators.js"; +import {Filter} from "../../filter/filter"; +import {css, html, HTMLTemplateResult} from "lit"; +import {gFilterService} from "../../filter/service"; + +@CustomElement() +export class FilterView extends FloatElement { + @property() + private filter!: Filter; + + static styles = [...FloatElement.styles, css` + .container { + display: flex; + justify-content: space-between; + align-items: center; + gap: 10px; + } + + .color-input {} + + .expression { + font-family: Consolas, sans-serif; + } + + .global-btn { + margin-left: auto; + } + + .remove-btn {} + `]; + + protected render(): HTMLTemplateResult { + return html` +
+ +
${this.filter.getExpression()}
+ + +
+ `; + } + + onColourChange(e: Event) { + this.filter = this.filter.setColour((e.target as HTMLInputElement).value) + gFilterService.upsert(this.filter); + this.requestUpdate(); + } + + onToggleGlobal(e: Event) { + this.filter.setIsGlobal(!this.filter.getIsGlobal()); + gFilterService.upsert(this.filter); + this.requestUpdate(); + } + + onRemove(e: Event) { + gFilterService.remove(this.filter); + } +} diff --git a/src/lib/components/injectors.ts b/src/lib/components/injectors.ts new file mode 100644 index 00000000..677e6c54 --- /dev/null +++ b/src/lib/components/injectors.ts @@ -0,0 +1,83 @@ +import {customElement} from 'lit/decorators.js'; +import {FloatElement} from "./custom"; +import {inPageContext} from "../utils/snips"; + +export enum InjectionMode { + // Injects once at page load for elements matching the selector + ONCE, + // Continually injects whenever new elements that match the + // selector exist that haven't been injected into yet + // + // Should be use for "dynamic" elements + CONTINUOUS +} + +enum InjectionType { + Append, + Before, + After, +} + +interface InjectionConfig { + exists: (ctx: JQuery, selector: string) => boolean; + op: (ctx: JQuery, target: typeof FloatElement) => void; +} + +const InjectionConfigs: {[key in InjectionType]: InjectionConfig} = { + [InjectionType.Append]: { + exists: (ctx, selector) => !!ctx.children(selector).length, + op: ((ctx, target) => ctx.append(target.elem())), + }, + [InjectionType.Before]: { + exists: (ctx, selector) => !!ctx.parent().children(selector).length, + op: ((ctx, target) => ctx.before(target.elem())), + }, + [InjectionType.After]: { + exists: (ctx, selector) => !!ctx.parent().children(selector).length, + op: ((ctx, target) => ctx.after(target.elem())), + }, +} + +export function CustomElement(): any { + return function (target: typeof FloatElement, propertyKey: string, descriptor: PropertyDescriptor) { + if (!inPageContext()) { + return; + } + customElement(target.tag())(target); + }; +} + +function Inject(selector: string, mode: InjectionMode, type: InjectionType): any { + return function (target: typeof FloatElement, propertyKey: string, descriptor: PropertyDescriptor) { + if (!inPageContext()) { + return; + } + switch (mode) { + case InjectionMode.ONCE: + $J(selector).each(function () { InjectionConfigs[type].op($J(this), target); }); + break; + case InjectionMode.CONTINUOUS: + setInterval(() => { + $J(selector).each(function () { + // Don't add the item again if we already have + if (InjectionConfigs[type].exists($J(this), target.tag())) return; + + InjectionConfigs[type].op($J(this), target); + }); + }, 250); + break; + } + }; +} + +export function InjectAppend(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { + return Inject(selector, mode, InjectionType.Append); +} + +export function InjectBefore(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { + return Inject(selector, mode, InjectionType.Before); +} + +export function InjectAfter(selector: string, mode: InjectionMode = InjectionMode.ONCE): any { + return Inject(selector, mode, InjectionType.After); +} diff --git a/src/lib/components/inventory/inventory_item_holder_metadata.ts b/src/lib/components/inventory/inventory_item_holder_metadata.ts new file mode 100644 index 00000000..42789245 --- /dev/null +++ b/src/lib/components/inventory/inventory_item_holder_metadata.ts @@ -0,0 +1,21 @@ +import {CustomElement, InjectAppend, InjectionMode} from "../injectors"; +import {Asset} from "../../types/steam"; +import {ItemHolderMetadata} from "../common/item_holder_metadata"; + +@CustomElement() +@InjectAppend('#active_inventory_page div.inventory_page:not([style*="display: none"]) .itemHolder div.app730', InjectionMode.CONTINUOUS) +export class InventoryItemHolderMetadata extends ItemHolderMetadata { + get asset(): Asset|undefined { + if (!this.assetId) return; + + return g_ActiveInventory?.m_rgAssets[this.assetId]?.description; + } + + get ownerSteamId(): string|undefined { + if (g_ActiveInventory?.m_owner) { + return g_ActiveInventory?.m_owner?.strSteamId; + } else if (g_ActiveInventory?.owner) { + return g_ActiveInventory?.owner?.strSteamId; + } + } +} diff --git a/src/lib/components/inventory/selected_item_info.ts b/src/lib/components/inventory/selected_item_info.ts new file mode 100644 index 00000000..08e501fe --- /dev/null +++ b/src/lib/components/inventory/selected_item_info.ts @@ -0,0 +1,173 @@ +import {FloatElement} from "../custom"; +import {CustomElement, InjectAfter, InjectionMode} from "../injectors"; +import {html, css, TemplateResult, HTMLTemplateResult} from "lit"; +import {state} from "lit/decorators.js"; +import {InventoryAsset} from "../../types/steam"; +import {gFloatFetcher} from "../../float_fetcher/float_fetcher"; +import {ItemInfo} from "../../bridge/handlers/fetch_inspect_info"; +import {formatSeed, isSkin, renderClickableRank} from "../../utils/skin"; +import {Observe} from "../../utils/observers"; +import {FetchStallResponse} from "../../bridge/handlers/fetch_stall"; +import {gStallFetcher} from "../../float_market/stall"; +import {Contract} from "../../types/float_market"; + +/** + * Why do we bind to iteminfo0 AND iteminfo1? + * + * Steam uses two divs that are interchanged (presumably to make a "fade" animation between them) for each selected + * item click. + */ +@CustomElement() +@InjectAfter('div#iteminfo0_content .item_desc_description div.item_desc_game_info', InjectionMode.CONTINUOUS) +@InjectAfter('div#iteminfo1_content .item_desc_description div.item_desc_game_info', InjectionMode.CONTINUOUS) +export class SelectedItemInfo extends FloatElement { + static styles = [...FloatElement.styles, css` + .container { + margin-bottom: 10px; + } + + .market-btn-container { + margin: 10px 0 10px 0; + padding: 5px; + width: fit-content; + border: 1px #5a5a5a solid; + background-color: #383838; + border-radius: 3px; + } + + .market-btn { + font-size: 15px; + display: flex; + align-items: center; + color: #ebebeb; + text-decoration: none; + } + `]; + + @state() + private itemInfo: ItemInfo|undefined; + + @state() + private loading: boolean = false; + + private stall: FetchStallResponse|undefined; + + get asset(): InventoryAsset|undefined { + return g_ActiveInventory?.selectedItem; + } + + get inspectLink(): string|undefined { + if (!this.asset) return; + + if (!this.asset.description?.actions || this.asset.description?.actions?.length === 0) return; + + return this.asset.description?.actions![0].link + .replace('%owner_steamid%', g_ActiveInventory?.m_owner?.strSteamId!) + .replace('%assetid%', this.asset.assetid!); + } + + get stallListing(): Contract|undefined { + if (!this.stall) { + return; + } + + return (this.stall.listings || []).find(e => e.item.asset_id === this.asset?.assetid); + } + + protected render(): HTMLTemplateResult { + if (this.loading) { + return html`
Loading...
`; + } + + if (!this.itemInfo) { + return html``; + } + + return html` +
+
Float: ${this.itemInfo.floatvalue.toFixed(14)} ${renderClickableRank(this.itemInfo)}
+
Paint Seed: ${formatSeed(this.itemInfo)}
+ ${this.renderListOnCSGOFloat()} + ${this.renderFloatMarketListing()} +
+ `; + } + + renderFloatMarketListing(): TemplateResult<1> { + if (!this.stallListing) { + return html``; + } + + return html` + + `; + } + + renderListOnCSGOFloat(): TemplateResult<1> { + if (this.stallListing) { + // Don't tell them to list it if it's already listed... + return html``; + } + + if (g_ActiveInventory?.m_owner?.strSteamId !== g_steamID) { + // Not the signed-in user, don't show + return html``; + } + + return html` + + `; + } + + async processSelectChange() { + // Reset state in-case they swap between skin and non-skin + this.itemInfo = undefined; + + if (!this.asset) return; + + if (!isSkin(this.asset.description)) return; + + // Commodities won't have inspect links + if (!this.inspectLink) return; + + try { + this.loading = true; + this.itemInfo = await gFloatFetcher.fetch({ + link: this.inspectLink + }); + } catch (e: any) { + console.error(`Failed to fetch float for ${this.asset.assetid}: ${e.toString()}`); + } finally { + this.loading = false; + } + } + + connectedCallback() { + super.connectedCallback(); + + // For the initial load, in case an item is pre-selected + this.processSelectChange(); + + Observe(() => this.asset, () => { + this.processSelectChange(); + }); + + if (g_ActiveInventory?.m_owner?.strSteamId) { + // Ignore errors + gStallFetcher.fetch({steam_id64: g_ActiveInventory?.m_owner.strSteamId}) + .then((stall) => this.stall = stall); + } + } +} diff --git a/src/lib/components/market/ad_banner.ts b/src/lib/components/market/ad_banner.ts new file mode 100644 index 00000000..0e9b47c6 --- /dev/null +++ b/src/lib/components/market/ad_banner.ts @@ -0,0 +1,108 @@ +import {CustomElement, InjectAfter, InjectionMode} from "../injectors"; +import {FloatElement} from "../custom"; +import {css, html, HTMLTemplateResult} from "lit"; +import {ClientSend} from "../../bridge/client"; +import {CSMoneyPrice, CSMoneyPriceResponse} from "../../bridge/handlers/csmoney_price"; +import {state} from "lit/decorators.js"; +import {AppId, ContextId} from "../../types/steam_constants"; + +@CustomElement() +export class AdBanner extends FloatElement { + static styles = [...FloatElement.styles, css` + .container { + padding: 10px; + margin-top: 10px; + background-color: rgba(0, 0, 0, 0.2); + text-align: center; + border: 1px solid black; + position: relative; + } + + .ad-notice { + position: absolute; + top: 3px; + right: 3px; + + } + + .link { + padding: 10px 10px 10px 10px; + background-color: transparent; + color: white; + font-family: 'Motiva Sans', Sans-serif, serif; + font-size: 18px; + text-decoration: none; + } + + .link img { + vertical-align: middle; + } + + .link .text { + vertical-align: bottom; + } + + .text .price { + font-weight: bold; + } + `]; + + @state() + private response: CSMoneyPriceResponse | undefined; + + getMarketHashName(): string|null { + if (Object.keys(g_rgAssets[AppId.CSGO][ContextId.PRIMARY]).length > 0) { + // Resistant to the user switching page languages + const firstAssetId = Object.keys(g_rgAssets[AppId.CSGO][ContextId.PRIMARY])[0]; + return g_rgAssets[AppId.CSGO][ContextId.PRIMARY][firstAssetId].market_hash_name; + } else if ((document.querySelector('.market_listing_item_name') as HTMLElement)?.innerText) { + // Fallback + return (document.querySelector('.market_listing_item_name') as HTMLElement)?.innerText; + } + + return null; + } + + protected render(): HTMLTemplateResult { + if (!this.response) return html``; + + if (!this.response.banner?.enable) { + return html``; + } + + if (this.response.banner.dynamic) { + return html` + + `; + } else { + return html` +
+
Ad
+ + + +
+ `; + } + } + + async connectedCallback() { + super.connectedCallback(); + + const marketHashName = this.getMarketHashName(); + if (!marketHashName) { + return; + } + + this.response = await ClientSend(CSMoneyPrice, {marketHashName}); + } +} diff --git a/src/lib/components/market/helpers.ts b/src/lib/components/market/helpers.ts new file mode 100644 index 00000000..b72037e0 --- /dev/null +++ b/src/lib/components/market/helpers.ts @@ -0,0 +1,89 @@ +import {Asset} from "../../types/steam"; +import {ItemInfo} from "../../bridge/handlers/fetch_inspect_info"; +import {AppId, ContextId} from "../../types/steam_constants"; + +/** + * If possible, constructs the inspect link from the given listing ID using page variables + * + * @param listingId ID for the listing, may also be referred to as "M" + */ +export function getMarketInspectLink(listingId: string): string|undefined { + const listingInfo = g_rgListingInfo[listingId]; + if (!listingInfo) return; + + const asset = g_rgAssets[AppId.CSGO][ContextId.PRIMARY][listingInfo.asset.id!]; + if (!asset || !asset.market_actions?.length) return; + + return asset.market_actions[0].link + .replace('%listingid%', listingId) + .replace('%assetid%', asset.id) +} + + +/** + * Inlines stickers into a market item row HTML showing the image and wear + * + * @param itemNameBlock Element with `.market_listing_item_name_block` + * @param itemInfo Item Info for the item from csgofloat API + * @param asset Steam Asset for the item + */ +export function inlineStickers(itemNameBlock: JQuery, itemInfo: ItemInfo, asset: Asset) { + if (!itemNameBlock) return; + + if (itemNameBlock.find('.csgofloat-stickers-container').length) { + // Don't inline stickers if they're already inlined + return; + } + + const lastDescription = asset.descriptions[asset.descriptions.length - 1]; + + if (lastDescription.type !== 'html' || !lastDescription.value.includes('sticker')) { + return; + } + + const nameMatch = lastDescription.value.match(/
([^<].*?): (.*)<\/center>/); + const imagesHtml = lastDescription.value.match(/()/g); + + if (!nameMatch || !imagesHtml) { + return; + } + + const stickerLang = nameMatch[1]; + const stickerNames = nameMatch[2].split(', '); + + const result = imagesHtml.map((imageHtml, i) => { + const url = + stickerLang === 'Sticker' + ? `https://steamcommunity.com/market/listings/730/${stickerLang} | ${stickerNames[i]}` + : `https://steamcommunity.com/market/search?q=${stickerLang} | ${stickerNames[i]}`; + + const sticker = itemInfo.stickers[i]; + + return ` + ${imagesHtml[i]} + + ${Math.round(100 * (sticker?.wear || 0)) + '%'} + + `; + }).reduce((acc, v) => acc + v, ''); + + + itemNameBlock.prepend(` +
+ ${result} +
+ `); +} + +/** + * Adds easy inspect link by hovering over a market listing row image + * @param itemImgContainer Element with ".market_listing_item_img_container" + * @param inspectLink Item Inspect Link + */ +export function inlineEasyInspect(itemImgContainer: JQuery, inspectLink: string|undefined) { + if (!itemImgContainer || !inspectLink) return; + + itemImgContainer.append(` + 🔍 + `); +} diff --git a/src/lib/components/market/item_row_wrapper.ts b/src/lib/components/market/item_row_wrapper.ts new file mode 100644 index 00000000..66c87200 --- /dev/null +++ b/src/lib/components/market/item_row_wrapper.ts @@ -0,0 +1,134 @@ +import {css, html} from 'lit'; + +import {state} from 'lit/decorators.js'; +import {CustomElement, InjectAppend, InjectionMode} from "../injectors"; +import {FloatElement} from "../custom"; +import {cache} from "decorator-cache-getter"; +import {Asset, ListingData} from "../../types/steam"; +import {gFloatFetcher} from "../../float_fetcher/float_fetcher"; +import {ItemInfo} from "../../bridge/handlers/fetch_inspect_info"; +import {getMarketInspectLink, inlineEasyInspect, inlineStickers} from "./helpers"; +import {formatSeed, renderClickableRank} from "../../utils/skin"; +import {gFilterService} from "../../filter/service"; +import {AppId, ContextId, Currency} from "../../types/steam_constants"; +import {defined} from "../../utils/checkers"; + +@CustomElement() +@InjectAppend("#searchResultsRows .market_listing_row .market_listing_item_name_block", InjectionMode.CONTINUOUS) +export class ItemRowWrapper extends FloatElement { + @cache + get listingId(): string|undefined { + const id = $J(this).parent().find(".market_listing_item_name").attr("id"); + const matches = id?.match(/listing_(\d+)_name/); + if (!matches || matches.length < 2) { + return; + } + + return matches[1]; + } + + get listingInfo(): ListingData|null { + return g_rgListingInfo[this.listingId!]; + } + + get asset(): Asset|undefined { + if (!this.listingInfo) return; + + return g_rgAssets[AppId.CSGO][ContextId.PRIMARY][this.listingInfo.asset.id!]; + } + + get inspectLink(): string|undefined { + return getMarketInspectLink(this.listingId!); + } + + async fetchFloat(): Promise { + return gFloatFetcher.fetch({ + link: this.inspectLink!, + listPrice: this.usdPrice + }); + } + + /** + * Returns the price of the item in the user's wallet currency + * + * If the user is not logged in, this will return undefined + */ + get convertedPrice(): number|undefined { + if (!defined(typeof g_rgWalletInfo) || !g_rgWalletInfo || !g_rgWalletInfo.wallet_currency) { + return; + } + + if (!this.listingInfo || !this.listingInfo.converted_price || !this.listingInfo.converted_fee) { + return; + } + + // Item currency is formatted as 20XX for most currencies where XX is the account currency + if (this.listingInfo.converted_currencyid !== (g_rgWalletInfo.wallet_currency + 2000)) { + return; + } + + return (this.listingInfo.converted_price + this.listingInfo.converted_fee) / 100; + } + + get usdPrice(): number|undefined { + if (this.listingInfo?.currencyid === Currency.USD) { + return this.listingInfo.price + this.listingInfo.fee; + } else if (this.listingInfo?.converted_currencyid === Currency.USD) { + return this.listingInfo.converted_price! + this.listingInfo.converted_fee!; + } + } + + @state() + private itemInfo: ItemInfo | undefined; + @state() + private error: string | undefined; + + async connectedCallback() { + super.connectedCallback(); + + // Only add if they don't have Steam Inventory Helper + if (!$J(this).parent().parent().find('.sih-inspect-magnifier').length) { + inlineEasyInspect( + $J(this).parent().parent().find('.market_listing_item_img_container'), + this.inspectLink); + } + + try { + this.itemInfo = await this.fetchFloat(); + } catch (e: any) { + this.error = e.toString(); + } + + if (this.itemInfo && this.asset) { + inlineStickers($J(this).parent().parent().find('.market_listing_item_name_block'), this.itemInfo, this.asset); + } + + if (this.itemInfo) { + gFilterService.onUpdate$.subscribe(() => { + const colour = gFilterService.matchColour(this.itemInfo!, this.convertedPrice) || ''; + $J(this).parent().parent().css('background-color', colour); + }); + } + + if (BuyItemDialog?.m_bInitialized && MarketCheckHash) { + // Does the hash now match an item on the page? + // Allows dynamic page scrubs to auto-show the dialog + MarketCheckHash(); + } + } + + render() { + if (this.itemInfo) { + return html` +
+ Float: ${this.itemInfo.floatvalue.toFixed(14)} ${renderClickableRank(this.itemInfo)}
+ Paint Seed: ${formatSeed(this.itemInfo)} +
+ `; + } else if (this.error) { + return html`
CSGOFloat ${this.error}
`; + } else { + return html`
Loading...
`; + } + } +} diff --git a/src/lib/components/market/page_size.ts b/src/lib/components/market/page_size.ts new file mode 100644 index 00000000..0946a489 --- /dev/null +++ b/src/lib/components/market/page_size.ts @@ -0,0 +1,54 @@ +import {FloatElement} from "../custom"; +import {CustomElement} from "../injectors"; +import {html, HTMLTemplateResult} from "lit"; +import '../common/ui/steam-button'; +import {query, state} from "lit/decorators.js"; +import {Get} from "../../bridge/handlers/storage_get"; +import {Set} from "../../bridge/handlers/storage_set"; +import {PAGE_SIZE} from "../../storage/keys"; + +@CustomElement() +export class PageSize extends FloatElement { + @state() + private selectedSize = 10; + + @state() + private sizes = [10, 25, 50, 100]; + + @query('select') + private select!: HTMLSelectElement; + + protected render(): HTMLTemplateResult { + return html` + + `; + } + + async connectedCallback() { + super.connectedCallback(); + + const size = await Get(PAGE_SIZE); + if (size) { + this.changePageSize(size); + } + } + + onSelect(e: Event) { + this.changePageSize(parseInt(this.select.value)); + } + + changePageSize(newSize: number) { + this.selectedSize = newSize; + g_oSearchResults.m_cPageSize = newSize; + g_oSearchResults.GoToPage(0, true); + + Set(PAGE_SIZE, newSize); + } +} diff --git a/src/lib/components/market/skin_viewer.ts b/src/lib/components/market/skin_viewer.ts new file mode 100644 index 00000000..f6950f48 --- /dev/null +++ b/src/lib/components/market/skin_viewer.ts @@ -0,0 +1,130 @@ +import {FloatElement} from "../custom"; +import {CustomElement, InjectAppend, InjectionMode} from "../injectors"; +import {css, html, HTMLTemplateResult, nothing} from "lit"; +import {FetchSkinModel, FetchSkinModelResponse} from "../../bridge/handlers/fetch_skin_model"; +import {state} from "lit/decorators.js"; +import {ClientSend} from "../../bridge/client"; + +import '../common/ui/steam-button'; +import {cache} from "decorator-cache-getter"; +import {getMarketInspectLink} from "./helpers"; + +enum Showing { + NONE, + MODEL, + SCREENSHOT, +} + +@CustomElement() +@InjectAppend('#searchResultsRows .market_listing_row', InjectionMode.CONTINUOUS) +export class SkinViewer extends FloatElement { + private response: FetchSkinModelResponse | undefined; + + static styles = [...FloatElement.styles, css` + .btn-container { + margin: 7px 0 5px 80px; + } + + .iframe-3d { + margin-top: 10px; + width: 100%; + height: 500px; + border-width: 0; + } + + img.screenshot { + width: 100%; + } + `]; + + @cache + get listingId(): string|undefined { + const id = $J(this).parent().attr("id"); + const matches = id?.match(/listing_(\d+)/); + if (!matches || matches.length < 2) { + return; + } + + return matches[1]; + } + + get inspectLink(): string|undefined { + return getMarketInspectLink(this.listingId!); + } + + @state() + private loading = false; + + @state() + private showing: Showing = Showing.NONE; + + async connectedCallback() { + super.connectedCallback(); + } + + loadingIfApplicable(text: string, type: Showing) { + if (this.showing == type && this.loading) { + return 'Loading...'; + } else { + return text; + } + } + + protected render(): HTMLTemplateResult { + return html` +
+ + + +
+ ${this.showing === Showing.MODEL && this.response?.modelLink ? html` +
+ +
+ ` : nothing} + + `; + } + + async fetchModel() { + this.loading = true; + try { + this.response = await ClientSend(FetchSkinModel, {inspectLink: this.inspectLink}); + } catch (e: any) { + alert(`Failed to fetch skin model: ${e.toString()}`); + } + this.loading = false; + } + + private toggle(type: Showing) { + if (this.showing === type) { + this.showing = Showing.NONE; + } else { + this.showing = type; + } + } + + async toggle3D() { + if (this.loading) return; + + this.toggle(Showing.MODEL); + + if (!this.response) { + await this.fetchModel(); + } + } + + async toggleScreenshot() { + if (this.loading) return; + + this.toggle(Showing.SCREENSHOT); + + if (!this.response) { + await this.fetchModel(); + } + } +} diff --git a/src/lib/components/market/sort_floats.ts b/src/lib/components/market/sort_floats.ts new file mode 100644 index 00000000..0ddf6304 --- /dev/null +++ b/src/lib/components/market/sort_floats.ts @@ -0,0 +1,92 @@ +import {FloatElement} from "../custom"; +import {CustomElement} from "../injectors"; +import {html, HTMLTemplateResult} from "lit"; +import '../common/ui/steam-button'; +import {state} from "lit/decorators.js"; +import {gFloatFetcher} from "../../float_fetcher/float_fetcher"; +import {getMarketInspectLink} from "./helpers"; +import {ItemInfo} from "../../bridge/handlers/fetch_inspect_info"; + +enum SortDirection { + NONE, + ASC, + DESC +} + +@CustomElement() +export class SortFloats extends FloatElement { + @state() + private direction: SortDirection = SortDirection.NONE; + + @state() + get buttonText(): string { + let txt = 'Sort by Float'; + + if (this.direction === SortDirection.ASC) { + txt += ' ▲'; + } else if (this.direction === SortDirection.DESC) { + txt += ' ▼'; + } + + return txt; + } + + + protected render(): HTMLTemplateResult { + return html` + + `; + } + + getNextSortDirection(): SortDirection { + switch (this.direction) { + case SortDirection.NONE: + return SortDirection.ASC; + case SortDirection.ASC: + return SortDirection.DESC; + case SortDirection.DESC: + return SortDirection.ASC; + } + } + + static sort(infos: { listingId: string; info: ItemInfo }[], direction: SortDirection): { listingId: string; info: ItemInfo }[] { + switch (direction) { + case SortDirection.NONE: + return infos; + case SortDirection.ASC: + return infos.sort((a, b) => a.info.floatvalue - b.info.floatvalue); + case SortDirection.DESC: + return infos.sort((a, b) => b.info.floatvalue - a.info.floatvalue); + } + } + + async onClick() { + const newDirection = this.getNextSortDirection(); + + const rows = document.querySelectorAll('#searchResultsRows .market_listing_row.market_recent_listing_row'); + + const infoPromises: Promise<{ listingId: string; info: ItemInfo }>[] = [...rows] + .map(e => e.id.replace('listing_', '')) + .map(async listingId => { + const link = getMarketInspectLink(listingId); + + const info = await gFloatFetcher.fetch({link: link!}); + return { + info, + listingId: listingId! + }; + }); + + const infos: { listingId: string; info: ItemInfo }[] = await Promise.all(infoPromises); + const sortedInfos = SortFloats.sort(infos, newDirection); + + let lastItem = document.querySelector('#searchResultsRows .market_listing_table_header'); + + for (const info of sortedInfos) { + const itemElement = document.querySelector(`#listing_${info.listingId}`); + lastItem = itemElement!.parentNode!.insertBefore(itemElement!, lastItem!.nextSibling); + } + + this.direction = newDirection; + } +} diff --git a/src/lib/components/market/utility_belt.ts b/src/lib/components/market/utility_belt.ts new file mode 100644 index 00000000..24e77dd2 --- /dev/null +++ b/src/lib/components/market/utility_belt.ts @@ -0,0 +1,52 @@ +import {FloatElement} from "../custom"; +import {CustomElement, InjectBefore, InjectionMode} from "../injectors"; +import {css, html, HTMLTemplateResult} from "lit"; +import '../common/ui/steam-button'; +import './page_size'; +import './sort_floats'; +import './ad_banner'; +import '../filter/filter_container'; + +@CustomElement() +@InjectBefore('#searchResultsRows', InjectionMode.ONCE) +export class UtilityBelt extends FloatElement { + get marketHashName(): string { + return (document.querySelector('.market_listing_nav a:nth-child(2)') as HTMLElement).innerText; + } + + static styles = [...FloatElement.styles, css` + .utility-container { + padding: 10px; + margin-top: 10px; + background-color: rgba(0, 0, 0, 0.2); + } + + .page-selector { + margin-left: 10px; + } + + .github { + margin-left: 10px; + text-decoration: underline; + font-family: 'Motiva Sans', sans-serif; + } + `] + + protected render(): HTMLTemplateResult { + return html` +
+ + + Powered by CSGOFloat +
+ +
+ + `; + } + + async connectedCallback() { + super.connectedCallback(); + } +} diff --git a/src/lib/components/trade_history/helpers.ts b/src/lib/components/trade_history/helpers.ts new file mode 100644 index 00000000..c10bde02 --- /dev/null +++ b/src/lib/components/trade_history/helpers.ts @@ -0,0 +1,121 @@ +function historyRowHashcode(row: HTMLElement): string { + const text = row.innerText.replace(/\W/g, ''); + + /* Based on https://stackoverflow.com/a/8831937 (Java's hashCode() method) */ + if (text.length === 0) { + return ''; + } + + let hash = 0; + for (let i = 0; i < text.length; i++) { + const char = text.charCodeAt(i); + hash = ((hash<<5)-hash)+char; + hash = hash & hash; + } + + return hash.toString(); +} + +function getTimestampFromTrade(row: HTMLElement): number|null { + const dateDiv = row.querySelector('.tradehistory_date'); + if (!dateDiv) { + return null; + } + + const date = dateDiv.firstChild!.nodeValue!.trim(); + const time = (dateDiv.querySelector('.tradehistory_timestamp')! as HTMLElement).innerText; + + const d = new Date(date); + const pure = time.replace('am', '').replace('pm', ''); + let hours = parseInt(pure.split(':')[0]); + const minutes = parseInt(pure.split(':')[1]); + if (time.includes('pm') && hours !== 12) { + /* Prevent 12:XXpm from getting 12 hours added */ + hours += 12 + } else if (time.includes('am') && hours === 12) { + /* Prevent 12:XXam from getting 12 hours instead of being 0 */ + hours -= 12 + } + + d.setHours(hours); + d.setMinutes(minutes); + return d.getTime() / 1000; +} + +async function hasTradeBeforeTime(hashCode: string, timestamp: number): Promise { + const resp = await fetch(`${location.protocol}//${location.host}${location.pathname}?after_time=${timestamp}&l=english`, { + credentials: 'same-origin' + }); + + const body = await resp.text(); + + if (body.includes('too many requests')) { + alert('You need to wait a couple seconds before generating the proof due to Valve rate-limits'); + throw 'Too many requests'; + } + + const doc = new DOMParser().parseFromString(body, 'text/html'); + const rows = doc.querySelectorAll('.tradehistoryrow') as NodeListOf; + + for (const row of rows) { + + const thisCode = historyRowHashcode(row); + if (thisCode === hashCode) { + return true; + } + } + + return false; +} + +async function fetchEnglishRow(index: number): Promise { + let queryParams = location.search; + if (queryParams === '') { + queryParams = '?l=english'; + } else { + queryParams += '&l=english'; + } + + /* Forces us to fetch the english version of the row at a given index no matter what */ + const resp = await fetch(`${location.protocol}//${location.host}${location.pathname}${queryParams}`, { + credentials: 'same-origin' + }); + + const body = await resp.text(); + + const doc = new DOMParser().parseFromString(body, 'text/html'); + const rows = doc.querySelectorAll('.tradehistoryrow'); + return rows[index] as HTMLElement; +} + +/** + * Returns the listing time of the row at {@param index} + * @param index Index of the trade history row on the page + */ +export async function fetchListingTime(index: number): Promise { + const node = await fetchEnglishRow(index); + const hashCode = historyRowHashcode(node); + + let timestamp; + + timestamp = getTimestampFromTrade(node); + if (!timestamp) { + throw 'failed timestamp creation'; + } + + let left = 0, right = 60; + let amt = 0; + while (left < right && amt < 5) { + const middle = left + Math.floor((right - left) / 2); + const hasTrade = await hasTradeBeforeTime(hashCode, timestamp + middle); + if (hasTrade) { + right = middle; + } else { + left = middle; + } + amt++; + } + + /* Hello to all the reversers */ + return timestamp + Math.floor((right + left) / 2); +} diff --git a/src/lib/components/trade_history/trade_proof.ts b/src/lib/components/trade_history/trade_proof.ts new file mode 100644 index 00000000..01ebea22 --- /dev/null +++ b/src/lib/components/trade_history/trade_proof.ts @@ -0,0 +1,44 @@ +import {html} from 'lit'; + +import {state} from 'lit/decorators.js'; +import {CustomElement, InjectAppend, InjectionMode} from "../injectors"; +import {FloatElement} from "../custom"; +import {fetchListingTime} from "./helpers"; +import '../common/ui/steam-button'; + +@CustomElement() +@InjectAppend(".tradehistoryrow .tradehistory_content", InjectionMode.CONTINUOUS) +export class TradeProof extends FloatElement { + @state() + private proofNumber: number | undefined; + + @state() + private isProcessing = false; + + async connectedCallback() { + super.connectedCallback(); + } + + render() { + return this.proofNumber ? html` + Proof: ${this.proofNumber} + ` : html` + + + `; + } + + private async onClick() { + this.isProcessing = true; + + const index = $J('.tradehistoryrow').index($J(this).parent().parent()); + try { + this.proofNumber = await fetchListingTime(index); + } catch (e) { + alert("Failed to parse time, make sure you're on an english version of the page by appending ?l=english to the url"); + } + this.isProcessing = false; + } +} diff --git a/src/lib/components/trade_offer/auto_fill.ts b/src/lib/components/trade_offer/auto_fill.ts new file mode 100644 index 00000000..1722e411 --- /dev/null +++ b/src/lib/components/trade_offer/auto_fill.ts @@ -0,0 +1,166 @@ +import {FloatElement} from "../custom"; +import {CustomElement, InjectBefore} from "../injectors"; +import {css, html, HTMLTemplateResult} from "lit"; +import {ClientSend} from "../../bridge/client"; +import {FetchPendingTrades, FetchPendingTradesResponse} from "../../bridge/handlers/fetch_pending_trades"; +import {Trade, TradeState} from "../../types/float_market"; +import {state} from "lit/decorators.js"; +import {Observe} from "../../utils/observers"; + +import '../common/ui/steam-button'; +import {AppId, ContextId} from "../../types/steam_constants"; + +@CustomElement() +@InjectBefore('div.trade_area') +export class AutoFill extends FloatElement { + @state() + private pendingTradesResponse: FetchPendingTradesResponse|undefined; + + static styles = [...FloatElement.styles, css` + .container { + margin-top: 10px; + margin-bottom: 10px; + padding: 15px; + background-color: rgb(48, 48, 48); + color: white; + display: flex; + justify-content: space-between; + align-items: center; + } + + .container.warning { + background-color: rgb(179, 0, 0); + } + + .float-icon { + float: left; + } + + .item-name { + font-size: 18px; + margin-left: 15px; + line-height: 32px; + } + + .sale-info { + padding-left: 45px; + color: darkgrey; + } + `]; + + async connectedCallback() { + super.connectedCallback(); + + try { + this.pendingTradesResponse = await ClientSend(FetchPendingTrades, {}); + } catch (e: any) { + console.error('failed to fetch pending trades on CSGOFloat Market, they are likely not logged in.', e.toString()); + } + + Observe(() => g_rgCurrentTradeStatus.me.assets.length, () => { + // Items they are giving changed, we can potentially hide/show an auto-fill dialog + this.requestUpdate(); + }); + } + + renderAutoFillDialog(trade: Trade): HTMLTemplateResult { + if (trade.state !== TradeState.PENDING) { + // Make sure they accepted the sale on CSGOFloat first + return html``; + } + + const item = trade.contract.item; + + if (g_rgCurrentTradeStatus.me.assets.find(a => a.assetid === item.asset_id)) { + // Item is already included in the trade offer + return html``; + } + + return html` +
+
+
+ +
+ + ${item.market_hash_name} + +
+ Detected Sale (Float: ${item.float_value.toFixed(12)}, Seed: ${item.paint_seed}) +
+
+ +
+ `; + } + + /** + * Show a warning to users if trade includes item with csgofloat note that doesn't match an existing sale + * + * Tries to prevent scenarios where malicious actors send offer with CSGOFloat text requesting an item + */ + showWarningDialog(): HTMLTemplateResult { + if (!this.hasAutoFillText()) { + return html``; + } + + const hasItemWithNoSale = g_rgCurrentTradeStatus.me.assets + .find(a => !this.pendingTradesResponse?.trades_to_send.find(b => b.contract.item.asset_id === a.assetid)); + + if (!hasItemWithNoSale) { + return html``; + } + + return html` +
+
+
+ +
+ + Warning! + +
+ Some of the items in the offer were not purchased from you on CSGOFloat Market (or you're logged into the wrong account) +
+
+
+ `; + } + + protected render(): HTMLTemplateResult { + if (!this.pendingTradesResponse) return html``; + + return html` + ${this.pendingTradesResponse.trades_to_send.map(e => this.renderAutoFillDialog(e))} + ${this.showWarningDialog()} + `; + } + + autoFill(trade: Trade) { + $J('#inventory_select_your_inventory').click(); + const el = UserYou?.findAsset(AppId.CSGO, ContextId.PRIMARY, trade.contract.item.asset_id)?.element; + if (!el) { + console.error('failed to find asset element for id ' + trade.contract.item.asset_id); + return; + } + + MoveItemToTrade(el); + + const note = document.getElementById('trade_offer_note'); + if (note) { + (note as HTMLTextAreaElement).value = `CSGOFloat Market Trade Offer #${trade.id} \n\nThanks for using CSGOFloat!`; + } + } + + hasAutoFillText(): boolean { + const tradeMessages = document.getElementsByClassName("included_trade_offer_note_ctn"); + if (tradeMessages.length > 0) { + const sanitized = (tradeMessages[0] as HTMLElement).innerText.trim().replace(/ /g, '').toLowerCase(); + + return sanitized.includes('csgofloat') || sanitized.includes('floatmarket'); + } + + return false; + } +} diff --git a/src/lib/components/trade_offer/trade_item_holder_metadata.ts b/src/lib/components/trade_offer/trade_item_holder_metadata.ts new file mode 100644 index 00000000..9d7a3d53 --- /dev/null +++ b/src/lib/components/trade_offer/trade_item_holder_metadata.ts @@ -0,0 +1,43 @@ +import {CustomElement, InjectAppend, InjectionMode} from "../injectors"; +import {Asset, UserSomeone} from "../../types/steam"; +import {ItemHolderMetadata} from "../common/item_holder_metadata"; +import {AppId, ContextId} from "../../types/steam_constants"; + +// Annotates item info (float, seed, etc...) in boxes on the Trade Offer Page +@CustomElement() +// Items when browsing their/your inventory +@InjectAppend('div.inventory_page:not([style*="display: none"]) .itemHolder div.app730', InjectionMode.CONTINUOUS) +// Items selected within the trade offer +@InjectAppend('.trade_offer .itemHolder div.app730', InjectionMode.CONTINUOUS) +export class TradeItemHolderMetadata extends ItemHolderMetadata { + get owningUser(): UserSomeone|undefined { + if (!this.assetId) return; + + if (UserThem && TradeItemHolderMetadata.getAssetFromUser(UserThem, this.assetId)) { + return UserThem; + } else if (UserYou && TradeItemHolderMetadata.getAssetFromUser(UserYou, this.assetId)) { + return UserYou; + } + } + + get ownerSteamId(): string|undefined { + if (!this.assetId) return; + + return this.owningUser?.strSteamId; + } + + get asset(): Asset|undefined { + if (!this.assetId) return; + + if (!this.owningUser) return; + + return TradeItemHolderMetadata.getAssetFromUser(this.owningUser, this.assetId); + } + + private static getAssetFromUser(user: UserSomeone, assetId: string): Asset|undefined { + if (user.rgContexts[AppId.CSGO][ContextId.PRIMARY].inventory?.rgInventory[assetId]) { + const inventory = user.rgContexts[AppId.CSGO][ContextId.PRIMARY].inventory; + return inventory?.rgInventory[assetId]; + } + } +} diff --git a/src/lib/filter/custom_functions.ts b/src/lib/filter/custom_functions.ts new file mode 100644 index 00000000..8de4addc --- /dev/null +++ b/src/lib/filter/custom_functions.ts @@ -0,0 +1,24 @@ +import {InternalInputVars} from "./types"; + +export function percentile(vars: InternalInputVars, rank: number) { + const minFloat = vars.minfloat > vars.minwearfloat ? vars.minfloat : vars.minwearfloat; + const maxFloat = vars.maxfloat < vars.maxwearfloat ? vars.maxfloat : vars.maxwearfloat; + const itemPercentile = 100 - (100 * (vars.float - minFloat)) / (maxFloat - minFloat); + + return itemPercentile > rank; +} + +export function percentileRange(vars: InternalInputVars, minRank: number, maxRank: number) { + const minFloat = vars.minfloat > vars.minwearfloat ? vars.minfloat : vars.minwearfloat; + const maxFloat = vars.maxfloat < vars.maxwearfloat ? vars.maxfloat : vars.maxwearfloat; + const itemPercentile = 100 - (100 * (vars.float - minFloat)) / (maxFloat - minFloat); + + return itemPercentile > minRank && itemPercentile < maxRank; +} + +export function match(str: string, regex: string) { + let thisMatch = str.toString().match(regex); + + if (thisMatch !== null) return thisMatch.length; + else return 0; +} diff --git a/src/lib/filter/filter.ts b/src/lib/filter/filter.ts new file mode 100644 index 00000000..05369e4e --- /dev/null +++ b/src/lib/filter/filter.ts @@ -0,0 +1,135 @@ +import {InternalInputVars, SerializedFilter} from "./types"; +import {match, percentile, percentileRange} from "./custom_functions"; +import {compileExpression} from "filtrex"; + +type ExpressionRunner = (data: InternalInputVars) => boolean; + +/** + * Encapsulates a filter, with mechanisms for running expressions + */ +export class Filter { + private colour: string; + private expression: string; + private isGlobal: boolean; + private runner: ExpressionRunner | undefined; + + private currentVars: InternalInputVars | undefined; + + constructor(expression: string, colour: string, isGlobal: boolean) { + this.expression = expression; + this.colour = colour; + this.isGlobal = isGlobal; + } + + static from(serialized: SerializedFilter): Filter { + return new Filter(serialized.expression, serialized.colour, serialized.isGlobal); + } + + setExpression(expression: string): Filter { + this.expression = expression; + this.runner = undefined; + return this; + } + + getExpression(): string { + return this.expression; + } + + setColour(colour: string): Filter { + this.colour = colour; + return this; + } + + getColour(): string { + return this.colour; + } + + setIsGlobal(isGlobal: boolean): Filter { + this.isGlobal = isGlobal; + return this; + } + + getIsGlobal(): boolean { + return this.isGlobal; + } + + serialize(): SerializedFilter { + return { + expression: this.expression, + colour: this.colour, + isGlobal: this.isGlobal || false, + } + } + + getExtraFunctions() { + return { + match: match, + percentile: (rank: number) => percentile(this.currentVars!, rank), + percentileRange: (minRank: number, maxRank: number) => percentileRange(this.currentVars!, minRank, maxRank) + } + } + + run(vars: InternalInputVars): any { + // Update vars in use for the functions + this.currentVars = vars; + + if (!this.runner) { + // Re-use the runner since it is expensive to create + this.runner = compileExpression(this.expression, {extraFunctions: this.getExtraFunctions()}); + } + + return this.runner(vars); + } + + /** + * Whether the return value from {@link run} is "valid" or usable + * for comparison purposes. + * + * For instance, will return false if `result` is an error indicating + * a property is undefined. + */ + static isValidReturnValue(result: any): boolean { + return typeof result === "boolean" || result === 0 || result === 1; + } + + /** + * Throws if the filter expression is invalid + */ + validate(): boolean { + // Use example values so we can trigger non-existent property errors + const result = this.run({ + float: 0.01, + seed: 999, + minfloat: 0.01, + maxfloat: 0.99, + minwearfloat: 0.01, + maxwearfloat: 0.99, + phase: 'Phase 1', + low_rank: 2, + high_rank: 2, + price: 10, + }); + + if (!Filter.isValidReturnValue(result)) { + throw new Error("invalid return type " + result.toString()); + } + + return true; + } + + /** + * Returns a boolean indicating if the filter expression is valid + */ + isValid(): boolean { + try { + this.validate(); + return true; + } catch (e) { + return false; + } + } + + equals(o: Filter) { + return this.expression === o.expression; + } +} diff --git a/src/lib/filter/service.ts b/src/lib/filter/service.ts new file mode 100644 index 00000000..6762e3ef --- /dev/null +++ b/src/lib/filter/service.ts @@ -0,0 +1,124 @@ +import {GLOBAL_FILTERS, StorageRow} from "../storage/keys"; +import {InternalInputVars, SerializedFilter} from "./types"; +import {Get} from "../bridge/handlers/storage_get"; +import {Filter} from "./filter"; +import {Set} from "../bridge/handlers/storage_set"; +import {ItemInfo} from "../bridge/handlers/fetch_inspect_info"; +import {rangeFromWear} from "../utils/skin"; +import {getDopplerPhase} from "../utils/dopplers"; +import {ReplaySubject} from "rxjs"; +import {debounce} from "lodash-decorators"; +import {averageColour} from "./utils"; +import {Remove} from "../bridge/handlers/storage_remove"; + + +/** + * Provides state for + */ +class FilterService { + private filters: Filter[] = []; + private itemRow: StorageRow | undefined; + /* Send last value upon subscription */ + private onUpdate = new ReplaySubject(1); + onUpdate$ = this.onUpdate.asObservable(); + + constructor() {} + + /** + * Initializes the service for the given item storage key + * + * This should be called before any other method + */ + async initialize(row: StorageRow) { + const globalFilters = (await Get(GLOBAL_FILTERS)) || []; + const itemFilters = (await Get(row)) || []; + this.filters = globalFilters.concat(itemFilters).map(e => Filter.from(e)); + this.itemRow = row; + this.onUpdate.next(this.filters); + } + + getFilters(): Filter[] { + return this.filters; + } + + matchColour(info: ItemInfo, price?: number): string|null { + const wearRange = rangeFromWear(info.floatvalue) || [0, 1]; + + const vars: InternalInputVars = { + float: info.floatvalue, + seed: info.paintseed, + minfloat: info.min, + maxfloat: info.max, + minwearfloat: wearRange[0], + maxwearfloat: wearRange[1], + phase: (getDopplerPhase(info.paintindex) || '').replace('Phase', '').trim(), + low_rank: info.low_rank!, + high_rank: info.high_rank! + }; + + if (price) { + vars.price = price; + } + + const colours = this.filters.filter(e => { + const result = e.run(vars); + if (!Filter.isValidReturnValue(result)) { + // If we fail to evaluate the expression, return false + // This is expected if they have an expression using `price` + // but are logged out. + return false; + } else { + return result; + } + }).map(e => e.getColour()); + if (colours.length === 0) { + return null; + } + + return averageColour(colours); + } + + remove(filter: Filter) { + this.filters = this.filters.filter(f => !f.equals(filter)); + this.save(); + this.onUpdate.next(this.filters); + } + + upsert(filter: Filter) { + const existingIndex = this.filters.findIndex((f) => f.equals(filter)); + if (existingIndex === -1) { + // Doesn't already exist, insert + this.filters.push(filter); + } else { + this.filters[existingIndex] = filter; + } + + this.save(); + this.onUpdate.next(this.filters); + } + + // Prevent spamming and hitting MAX_WRITE_OPERATIONS_PER_MINUTE + @debounce(500) + private async save() { + if (!this.itemRow) { + throw new Error('cannot save filters without being initialized'); + } + + const gFilters = this.filters.filter(f => f.getIsGlobal()).map(f => f.serialize()); + + await Set(GLOBAL_FILTERS, gFilters); + + const iFilters = this.filters.filter(f => !f.getIsGlobal()).map(f => f.serialize()); + + if (iFilters.length === 0) { + // Remove the key to prevent polluting their storage + // Sync storage has a max of 512 keys which we don't want to hit easily + await Remove(this.itemRow); + } else { + await Set(this.itemRow, iFilters); + } + + } +} + +export const gFilterService = new FilterService(); diff --git a/src/lib/filter/types.ts b/src/lib/filter/types.ts new file mode 100644 index 00000000..68617ee8 --- /dev/null +++ b/src/lib/filter/types.ts @@ -0,0 +1,19 @@ + +export interface InternalInputVars { + float: number; + seed: number; + minfloat: number; + maxfloat: number; + minwearfloat: number; + maxwearfloat: number; + phase: string; + low_rank: number; + high_rank: number; + price?: number; +} + +export interface SerializedFilter { + expression: string; + colour: string; + isGlobal: boolean; +} diff --git a/src/lib/filter/utils.ts b/src/lib/filter/utils.ts new file mode 100644 index 00000000..3d0fbf81 --- /dev/null +++ b/src/lib/filter/utils.ts @@ -0,0 +1,35 @@ +function hexToRgb(hex: string): number[]|null { + let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null; +} + +function rgbToHex(rgb: number[]): string { + return '#' + ((1 << 24) + (rgb[0] << 16) + (rgb[1] << 8) + rgb[2]).toString(16).slice(1); +} + +/** + * Given an array of colours in the hex format, averages them + * to produce a hex output + * + * @param hexColours Colours in the format "#AAAAA" + */ +export function averageColour(hexColours: string[]): string { + // Get the average colour between each matching filter + const average: number[] = [0, 0, 0]; + + for (const colour of hexColours) { + const rgb = hexToRgb(colour); + if (!rgb) { + continue; + } + + for (let i = 0; i < 3; i++) { + average[i] += rgb[i]; + } + } + + return rgbToHex(average.map(e => e / hexColours.length)); +} + + + diff --git a/src/lib/float_fetcher/float_fetcher.ts b/src/lib/float_fetcher/float_fetcher.ts new file mode 100644 index 00000000..22a0f34f --- /dev/null +++ b/src/lib/float_fetcher/float_fetcher.ts @@ -0,0 +1,27 @@ +import {CachedQueue, Job} from "../utils/queue"; +import {ClientSend} from "../bridge/client"; +import {FetchInspectInfo, FetchInspectInfoRequest, ItemInfo} from "../bridge/handlers/fetch_inspect_info"; + +class InspectJob extends Job { + hashCode(): string { + return this.data.link; + } +} + +class FloatFetcher extends CachedQueue { + constructor() { + /** allow up to 10 simultaneous float fetch reqs */ + super(10); + } + + fetch(req: FetchInspectInfoRequest): Promise { + return this.add(new InspectJob(req)); + } + + protected async process(req: FetchInspectInfoRequest): Promise { + const resp = await ClientSend(FetchInspectInfo, req); + return resp.iteminfo; + } +} + +export const gFloatFetcher = new FloatFetcher(); diff --git a/src/lib/float_market/stall.ts b/src/lib/float_market/stall.ts new file mode 100644 index 00000000..4b52bec2 --- /dev/null +++ b/src/lib/float_market/stall.ts @@ -0,0 +1,25 @@ +import {CachedQueue, GenericJob} from "../utils/queue"; +import { + FetchStall, + FetchStallRequest, + FetchStallResponse, +} from "../bridge/handlers/fetch_stall"; +import {ClientSend} from "../bridge/client"; + + +class StallFetcher extends CachedQueue { + fetch(req: FetchStallRequest): Promise { + return this.add(new GenericJob(req)); + } + + protected async process(req: FetchStallRequest): Promise { + try { + return await ClientSend(FetchStall, req); + } catch (e) { + // Stub out to prevent future calls + return {listings: []}; + } + } +} + +export const gStallFetcher = new StallFetcher(1); diff --git a/src/lib/page_scripts/inventory.ts b/src/lib/page_scripts/inventory.ts new file mode 100644 index 00000000..f21bfd82 --- /dev/null +++ b/src/lib/page_scripts/inventory.ts @@ -0,0 +1,107 @@ +import {init} from "./utils"; +import "../components/inventory/inventory_item_holder_metadata"; +import "../components/inventory/selected_item_info"; + +init('src/lib/page_scripts/inventory.js', main); + +async function main() { + injectInventoryFallback(); +} + +function injectInventoryFallback() { + /* + Steam inventories are a mess, adds a fallback onto a deprecated inventory endpoint + We effectively just override some of the inventory URL functions + */ + + let g_InventoryFallbackCSGOFloat = false; + + CInventory.prototype.g_GetInventoryLoadURL = CInventory.prototype.GetInventoryLoadURL; + CInventory.prototype.g_AddInventoryData = CInventory.prototype.AddInventoryData; + CInventory.prototype.g_ShowInventoryLoadError = CInventory.prototype.ShowInventoryLoadError; + + CInventory.prototype.GetInventoryLoadURL = function CInventory_GetInventoryLoadURL_CSGOFloat() { + if (g_InventoryFallbackCSGOFloat) { + return `https://steamcommunity.com/profiles/${this.m_steamid}/inventory/json/${this.m_appid}/${this.m_contextid}`; + } else { + /* Fallback to the upstream method */ + return this.g_GetInventoryLoadURL(); + } + }; + + interface FallbackAsset { + id: number; + contextid: number; + assetid: number; + classid: number; + instanceid: number; + amount: number; + pos: number; + } + interface FallbackResponse { + success: boolean; + rgInventory: {[assetId: string]: FallbackAsset}; + rgDescriptions: any; + } + + CInventory.prototype.AddInventoryData = function CInventory_AddInventoryData_CSGOFloat(data: FallbackResponse) { + if (!g_InventoryFallbackCSGOFloat) { + /* upstream can handle */ + return this.g_AddInventoryData(data); + } + + /* Preprocess the data to match the other inventory format */ + if (!data || !data.success) { + alert('failed to fetch inventory'); + return; + } + + const assets = Object.values(data.rgInventory).map(asset => { + return { + appid: this.m_appid, + contextid: this.m_contextid, + assetid: asset.id, + classid: asset.classid, + instanceid: asset.instanceid, + amount: asset.amount, + m_pos: asset.pos, + }; + }).sort((a, b) => a.m_pos - b.m_pos); + + const transformedData = { + assets, + descriptions: Object.values(data.rgDescriptions), + total_inventory_count: Math.max(...assets.map(e => e.m_pos)), + success: true, + more_items: 0, + rwgrsn: -2 + }; + + /* Required to force the page to lazy load images correctly */ + this.m_bNeedsRepagination = true; + + return this.g_AddInventoryData(transformedData); + }; + + CInventory.prototype.ShowInventoryLoadError = function CInventory_ShowInventoryLoadError_CSGOFloat() { + const prev_$ErrorDisplay = this.m_$ErrorDisplay; + + /* Handle upstream like before */ + this.g_ShowInventoryLoadError(); + + if (prev_$ErrorDisplay) { + /* Element already created, nothing special to do */ + return; + } + + this.m_$ErrorDisplay.find(".retry_load_btn").after(` +
+ Try Again using CSGOFloat +
+ `); + this.m_$ErrorDisplay.find(".retry_load_btn_csgofloat").click(() => { + g_InventoryFallbackCSGOFloat = true; + this.RetryLoad() + }); + }; +} diff --git a/src/lib/page_scripts/market_listing.ts b/src/lib/page_scripts/market_listing.ts new file mode 100644 index 00000000..51e6ea37 --- /dev/null +++ b/src/lib/page_scripts/market_listing.ts @@ -0,0 +1,9 @@ +import {init} from "./utils"; +import "../components/market/item_row_wrapper"; +import "../components/market/skin_viewer"; +import "../components/market/utility_belt"; + +init('src/lib/page_scripts/market_listing.js', main); + +async function main() {} + diff --git a/src/lib/page_scripts/trade_history.ts b/src/lib/page_scripts/trade_history.ts new file mode 100644 index 00000000..6873b695 --- /dev/null +++ b/src/lib/page_scripts/trade_history.ts @@ -0,0 +1,6 @@ +import {init} from "./utils"; +import "../components/trade_history/trade_proof"; + +init('src/lib/page_scripts/trade_history.js', main); + +async function main() {} diff --git a/src/lib/page_scripts/trade_offer.ts b/src/lib/page_scripts/trade_offer.ts new file mode 100644 index 00000000..bff6937e --- /dev/null +++ b/src/lib/page_scripts/trade_offer.ts @@ -0,0 +1,7 @@ +import {init} from "./utils"; +import "../components/trade_offer/trade_item_holder_metadata"; +import "../components/trade_offer/auto_fill"; + +init('src/lib/page_scripts/trade_offer.js', main); + +async function main() {} diff --git a/src/lib/page_scripts/utils.ts b/src/lib/page_scripts/utils.ts new file mode 100644 index 00000000..f5d358b1 --- /dev/null +++ b/src/lib/page_scripts/utils.ts @@ -0,0 +1,35 @@ +import {ExecuteScriptOnPage} from "../bridge/handlers/execute_script"; +import {ClientSend} from "../bridge/client"; +import {inPageContext} from "../utils/snips"; +import {ExecuteCssOnPage} from "../bridge/handlers/execute_css"; + +/** + * Initializes a page script, executing it in the page context if necessary + * + * @param scriptPath Relative path of the script (always in .js) + * @param ifPage Fn to run if we are in the page's execution context + */ +export function init(scriptPath: string, ifPage: ()=>any) { + // Don't allow the page script to run this. + if (inPageContext()) { + // @ts-ignore Set global identifier for other extensions to use + window.csgofloat = true; + + ifPage(); + return; + } + + // Global styles + ClientSend(ExecuteCssOnPage, { + path: 'src/global.css' + }); + + ClientSend(ExecuteScriptOnPage, { + path: scriptPath + }); + + console.log(`%c CSGOFloat Market Checker (v${chrome.runtime.getManifest().version}) by Step7750 `, + 'background: #004594; color: #fff;'); + console.log('%c Changelog can be found here: https://github.com/csgofloat/extension ', + 'background: #004594; color: #fff;'); +} diff --git a/src/lib/storage/keys.ts b/src/lib/storage/keys.ts new file mode 100644 index 00000000..52e4d0a5 --- /dev/null +++ b/src/lib/storage/keys.ts @@ -0,0 +1,46 @@ +/** + * Keys for use as the raw "key" in local/sync storage for a row + */ +import {SerializedFilter} from "../filter/types"; + +export enum StorageKey { + // Backwards compatible with <3.0.0 + PAGE_SIZE = 'pageSize', + ITEM_FILTERS = 'expressions', + GLOBAL_FILTERS = 'global' +} + +export type DynamicStorageKey = string; + +/** + * Encapsulates a key/value pair, each key has a value associated + */ +export interface StorageRow { + key: StorageKey|DynamicStorageKey; +} + +function newRow(name: StorageKey): StorageRow { + return {key: name} as StorageRow; +} + +/** + * Allows defining a "dynamic" row that has different keys at runtime, but share a similar + * type. + * + * NOTE: This is generally **discouraged** and you should instead store under a static key with + * an object of your desire. It exists to be compatible with historical poor decisions. + * + * @param suffix Storage key used as a suffix for the internal storage key + */ +function newDynamicRow(suffix: StorageKey): (prefix: string) => StorageRow { + return function (prefix: string) { + return {key: `${prefix}_${suffix}`} as StorageRow; + } +} + +// Explicitly create each row here that is used in the application +// This is designed to have type safety for all operations on the same key +export const PAGE_SIZE = newRow(StorageKey.PAGE_SIZE); +// Dynamic prefixes should be the market hash name of the item +export const DYNAMIC_ITEM_FILTERS = newDynamicRow(StorageKey.ITEM_FILTERS); +export const GLOBAL_FILTERS = newRow(StorageKey.GLOBAL_FILTERS); diff --git a/src/lib/storage/store.ts b/src/lib/storage/store.ts new file mode 100644 index 00000000..33260f72 --- /dev/null +++ b/src/lib/storage/store.ts @@ -0,0 +1,27 @@ +import {DynamicStorageKey, StorageKey} from "./keys"; + +class Store { + // Prefer to use sync storage if possible + get storage(): chrome.storage.SyncStorageArea|chrome.storage.LocalStorageArea { + return chrome.storage.sync ? chrome.storage.sync : chrome.storage.local; + } + + async get(key: StorageKey|DynamicStorageKey): Promise { + const a = await this.storage.get(key); + if (!a || !(key in a)) { + return null; + } + + return JSON.parse(a[key]) as T; + } + + async set(key: StorageKey|DynamicStorageKey, value: T): Promise { + return this.storage.set({[key]: JSON.stringify(value)}); + } + + async remove(key: StorageKey|DynamicStorageKey): Promise { + return this.storage.remove([key]); + } +} + +export const gStore = new Store(); diff --git a/src/lib/types/extension_globals.ts b/src/lib/types/extension_globals.ts new file mode 100644 index 00000000..7b4b5343 --- /dev/null +++ b/src/lib/types/extension_globals.ts @@ -0,0 +1,8 @@ +export {}; + +declare global { + interface Window { + CSGOFLOAT_EXTENSION_ID: string; + CSGOFLOAT_MODEL_FRAME_URL: string; + } +} diff --git a/src/lib/types/float_market.ts b/src/lib/types/float_market.ts new file mode 100644 index 00000000..c5ab56d4 --- /dev/null +++ b/src/lib/types/float_market.ts @@ -0,0 +1,98 @@ +/** + * Types related to CSGOFloat Market + */ + +export interface Item { + asset_id: string; + d_param: string; + def_index: number; + description: string; + float_value: number; + has_screenshot: boolean; + high_rank: number; + icon_url: string; + inspect_link: string; + is_souvenir: false; + is_stattrak: false; + item_name: string; + low_rank: number; + market_hash_name: string; + paint_index: number; + paint_seed: number; + phase?: string; + quality: number; + rarity: number; + wear_name: string; + scm?: { + price?: number; + volume?: number; + }; +} + +export interface User { + avatar: string; + flags: number; + online: boolean; + stall_public: boolean; + statistics: { + median_trade_time: number; + total_avoided_trades: number; + total_failed_trades: number; + total_trades: number; + total_verified_trades: number; + } + steam_id: string; + username: string; +} + +export enum ContractState { + SOLD = 'sold', + LISTED = 'listed', + DELISTED = 'delisted', + REFUNDED = 'refunded' +} + +export enum ContractType { + BUY_NOW = 'buy_now', + AUCTION = 'auction' +} + +export interface Contract { + created_at: string; + id: string; + is_seller: boolean; + is_watchlisted: boolean; + item: Item; + max_offer_discount?: number; + min_offer_price?: number; + price: number; + seller: User; + state: ContractState; + type: ContractType; + watchers: number; +} + +export enum TradeState { + QUEUED = 'queued', + PENDING = 'pending', + VERIFIED = 'verified', + FAILED = 'failed', + CANCELLED = 'cancelled' +} + +export interface Trade { + id: string; + accepted_at?: string; + buyer: User; + buyer_id: string; + contract: Contract; + contract_id: string; + created_at: string; + expires_at?: string; + grace_period_start: string; + manual_verification: boolean; + manual_verification_at?: string; + seller_id: string; + state: TradeState; + trade_url: string; +} diff --git a/src/lib/types/steam.d.ts b/src/lib/types/steam.d.ts new file mode 100644 index 00000000..c477c8f2 --- /dev/null +++ b/src/lib/types/steam.d.ts @@ -0,0 +1,202 @@ +import $ from "jquery"; +import {AppId, ContextId} from "./steam_constants"; + + +export interface Action { + link: string; + name: string; +} + +// g_rgListingInfo +export interface ListingData { + listingid: string; + fee: number; + price: number; + currencyid: number; + converted_price?: number; + converted_fee?: number; + converted_currencyid?: number; + steam_fee: number; + asset: { + amount: string; + appid: AppId; + currency: number; + id: string; + market_actions: Action[]; + }; +} + +// g_rgWalletInfo +export interface WalletInfo { + success: number; + wallet_country: string; + wallet_currency: number; +} + +// g_rgAssets +export interface Asset { + amount: number; + app_icon: string; + appid: AppId; + background_color: string; + classid: string; + commodity: number; + contextid: string; + currency: number; + descriptions: { + type: string; + value: string; + }[]; + icon_url: string; + icon_url_large: string; + id: string; + instanceid: string; + is_stackable: boolean; + market_actions?: Action[]; + actions?: Action[]; + market_hash_name: string; + market_name: string; + market_tradable_restriction: number; + marketable: number; + name: string; + name_color: string; + original_amount: string; + owner: number; + status: number; + tradable: number; + type: string; + unowned_contextid: string; + unowned_id: string; + tags?: { + category: string; + internal_name: string; + localized_category_name?: string; + localized_tag_name?: string; + }[]; + element?: HTMLElement; +} + +export interface InventoryAsset { + amount: string; + appid: AppId; + assetid: string; + classid: string; + contextid: string; + description: Asset; + element: HTMLElement; + homeElement: HTMLElement; + instanceid: string; + is_currency: boolean; +} + +// g_ActiveInventory.m_owner +export interface mOwner { + strSteamId: string; +} + +// g_ActiveInventory +export interface CInventory { + initialized: boolean; + m_rgAssets: {[assetId: string]: InventoryAsset}; + rgInventory: {[assetId: string]: Asset}; + m_owner?: mOwner; + owner?: mOwner; + selectedItem?: InventoryAsset; +} + +export interface CAjaxPagingControls { + m_bLoading: boolean; + m_cMaxPages: number; + m_cPageSize: number; + m_cTotalCount: number; + m_iCurrentPage: number; + m_strClassPrefix: string; + m_strDefaultAction: string; + m_strElementPrefix: string; + GoToPage: (iPage: number, bForce: boolean) => any; + NextPage: () => any; + PrevPage: () => any; +} + +export interface BuyItemDialog { + m_bInitialized: number; + m_bPurchaseClicked: number; + m_bPurchaseSuccess: number; +} + +export interface CInventory { + prototype: { + GetInventoryLoadURL: () => string; + AddInventoryData: (data: any) => void; + ShowInventoryLoadError: () => void; + RetryLoad: () => any; + + // Annotated by CSGOFloat, see {@link fallback.ts} + g_ShowInventoryLoadError: () => void; + g_AddInventoryData: (data: any) => void; + g_GetInventoryLoadURL: () => string; + + m_steamid: string; + m_appid: number; + m_contextid: number; + m_bNeedsRepagination: boolean; + m_$ErrorDisplay: JQuery; + } +} + +export interface RgContext { + asset_count: number; + id: string; + inventory: CInventory; +} + +export interface UserSomeone { + bReady: boolean; + rgContexts: { + [AppId.CSGO]: { + [ContextId.PRIMARY]: RgContext + } + }; + strSteamId: string; + findAsset: (appId: AppId, contextId: ContextId, itemId: string) => Asset; +} + +export interface CurrentTradeAsset { + amount: number; + appid: AppId; + assetid: string; + contextid: string; +} + +export interface CurrentTradeStatus { + newversion: boolean; + version: number; + me: { + assets: CurrentTradeAsset[]; + ready: boolean; + }; + them: { + assets: CurrentTradeAsset[]; + ready: boolean; + }; +} + +// Declares globals available in the Steam Page Context +declare global { + const $J: typeof $; + const g_rgListingInfo: {[listingId: string]: ListingData}; + const g_rgWalletInfo: WalletInfo|undefined; // Not populated when user is signed-out + const g_rgAssets: {[appId in AppId]: {[contextId in ContextId]: {[assetId: string]: Asset}}}; + const g_ActiveInventory: CInventory|undefined; // Only populated on Steam inventory pages + const g_steamID: string; + const g_oSearchResults: CAjaxPagingControls; + const BuyItemDialog: BuyItemDialog|undefined; // Only populated on Steam Market pages + const MarketCheckHash: (() => any)|undefined; // Only populated on Steam Market pages + const CInventory: CInventory; + const UserThem: UserSomeone|undefined; // Only populated on create offer pages + const UserYou: UserSomeone|undefined; // Only populated on create offer pages + const MoveItemToTrade: (el: HTMLElement) => void; // Only populated on create offer pages + const g_rgCurrentTradeStatus: CurrentTradeStatus; +} + +export {}; diff --git a/src/lib/types/steam_constants.ts b/src/lib/types/steam_constants.ts new file mode 100644 index 00000000..e4fb493e --- /dev/null +++ b/src/lib/types/steam_constants.ts @@ -0,0 +1,12 @@ +// See g_rgCurrencyData +export enum Currency { + USD = 2001 +} + +export enum AppId { + CSGO = 730 +} + +export enum ContextId { + PRIMARY = 2 +} diff --git a/src/lib/utils/cache.ts b/src/lib/utils/cache.ts new file mode 100644 index 00000000..721aa910 --- /dev/null +++ b/src/lib/utils/cache.ts @@ -0,0 +1,30 @@ +/** + * Simple Generic Cache with stringified keys + */ +export class Cache { + private cache_: {[key: string]: T} = {}; + + set(key: string, value: T) { + this.cache_[key] = value; + } + + get(key: string): T|undefined { + return this.cache_[key]; + } + + getOrThrow(key: string): T { + if (!this.has(key)) { + throw new Error(`key ${key} does not exist in map [getOrThrow]`); + } + + return this.cache_[key]; + } + + has(key: string): boolean { + return key in this.cache_; + } + + size(): number { + return Object.keys(this.cache_).length; + } +} diff --git a/src/lib/utils/checkers.ts b/src/lib/utils/checkers.ts new file mode 100644 index 00000000..391c74e0 --- /dev/null +++ b/src/lib/utils/checkers.ts @@ -0,0 +1,3 @@ +export function defined(t: string): boolean { + return t !== 'undefined'; +} diff --git a/src/lib/utils/deferred_promise.ts b/src/lib/utils/deferred_promise.ts new file mode 100644 index 00000000..60dfa7cd --- /dev/null +++ b/src/lib/utils/deferred_promise.ts @@ -0,0 +1,28 @@ + +/** + * Similar to a promise, but allows the ability to resolve/reject in a different context + * */ +export class DeferredPromise { + private resolve_: ((value: T) => void) | undefined; + private reject_: ((reason: string) => void) | undefined; + private readonly promise_: Promise; + + constructor() { + this.promise_ = new Promise((resolve, reject) => { + this.resolve_ = resolve; + this.reject_ = reject; + }); + } + + resolve(value: T) { + this.resolve_!(value); + } + + reject(reason: string) { + this.reject_!(reason); + } + + promise(): Promise { + return this.promise_; + } +} diff --git a/src/lib/utils/dopplers.ts b/src/lib/utils/dopplers.ts new file mode 100644 index 00000000..7c791211 --- /dev/null +++ b/src/lib/utils/dopplers.ts @@ -0,0 +1,34 @@ +const dopplerPhases: {[paintIndex: number]: string} = { + 418: 'Phase 1', + 419: 'Phase 2', + 420: 'Phase 3', + 421: 'Phase 4', + 415: 'Ruby', + 416: 'Sapphire', + 417: 'Black Pearl', + 569: 'Phase 1', + 570: 'Phase 2', + 571: 'Phase 3', + 572: 'Phase 4', + 568: 'Emerald', + 618: 'Phase 2', + 619: 'Sapphire', + 617: 'Black Pearl', + 852: 'Phase 1', + 853: 'Phase 2', + 854: 'Phase 3', + 855: 'Phase 4', + 1119: "Emerald", + 1120: "Phase 1", + 1121: "Phase 2", + 1122: "Phase 3", + 1123: "Phase 4" +}; + +export function hasDopplerPhase(paintIndex: number) { + return paintIndex in dopplerPhases; +} + +export function getDopplerPhase(paintIndex: number): string|undefined { + return dopplerPhases[paintIndex]; +} diff --git a/src/lib/utils/observers.ts b/src/lib/utils/observers.ts new file mode 100644 index 00000000..6995ee8b --- /dev/null +++ b/src/lib/utils/observers.ts @@ -0,0 +1,11 @@ +export function Observe(computeObject: () => T, cb: () => any, pollRateMs = 50) { + let prev = computeObject(); + + setInterval(() => { + const now = computeObject(); + if (prev !== now) { + cb(); + } + prev = now; + }, pollRateMs); +} diff --git a/src/lib/utils/queue.ts b/src/lib/utils/queue.ts new file mode 100644 index 00000000..2034aa39 --- /dev/null +++ b/src/lib/utils/queue.ts @@ -0,0 +1,116 @@ +import {Cache} from "./cache"; +import {DeferredPromise} from "./deferred_promise"; + +export abstract class Job { + constructor(protected data: T) {} + + getData() { + return this.data; + } + + /** + * Hash that uniquely identifies this job. + * + * If two jobs have the same hashcode, they are considered identical. + * */ + hashCode(): string { + return JSON.stringify(this.data); + } +} + +export class GenericJob extends Job {} + +interface QueuedJob { + job: Job; + deferredPromise: DeferredPromise; +} + +/** + * Queue to handle processing of "Jobs" with a request that + * return a response. Ensures a max concurrency of processing + * simultaneous jobs. + */ +export abstract class Queue { + private internalQueue: QueuedJob[] = []; + private jobsProcessing: number = 0; + + constructor(private maxConcurrency: number) {} + + /** Amount of jobs currently in the queue */ + size(): number { + return this.internalQueue.length; + } + + has(job: Job): boolean { + return !!this.internalQueue.find(e => e.job.hashCode() === job.hashCode()); + } + + getOrThrow(job: Job): QueuedJob { + if (!this.has(job)) { + throw new Error(`Job[${job.hashCode()}] is not queued`); + } + + // Guaranteed + return this.internalQueue.find(e => e.job.hashCode() === job.hashCode())!; + } + + async checkQueue() { + if (this.internalQueue.length === 0 || this.jobsProcessing >= this.maxConcurrency) { + // Don't want to launch more instances + return; + } + + this.jobsProcessing += 1; + + const queuedJob = this.internalQueue.shift()!; + const req: Req = queuedJob.job.getData(); + + try { + const resp = await this.process(req); + queuedJob.deferredPromise.resolve(resp); + } catch (e) { + queuedJob.deferredPromise.reject((e as any).toString()); + } + + this.jobsProcessing -= 1; + this.checkQueue(); + } + + add(job: Job): Promise { + if (this.has(job)) { + return this.getOrThrow(job)?.deferredPromise.promise(); + } + + const promise = new DeferredPromise(); + this.internalQueue.push({job, deferredPromise: promise}); + + setTimeout(() => this.checkQueue(), 0); + + return promise.promise(); + } + + protected abstract process(req: Req): Promise; +} + +// Like a queue, but has an internal cache for elements already requested +export abstract class CachedQueue extends Queue{ + private cache = new Cache(); + + /** Amount of previously requested jobs stored in the cache */ + cacheSize(): number { + return this.cache.size(); + } + + add(job: Job): Promise { + if (this.cache.has(job.hashCode())) { + return Promise.resolve(this.cache.getOrThrow(job.hashCode())); + } + + return super.add(job).then((resp) => { + this.cache.set(job.hashCode(), resp); + return resp; + }); + } + + protected abstract process(req: Req): Promise; +} diff --git a/src/lib/utils/ranks.ts b/src/lib/utils/ranks.ts new file mode 100644 index 00000000..fdd6c629 --- /dev/null +++ b/src/lib/utils/ranks.ts @@ -0,0 +1,14 @@ + export function getRankColour(rank: number) { + switch (rank) { + case 1: + return '#c3a508'; + case 2: + case 3: + return '#9a9999'; + case 4: + case 5: + return '#8a5929'; + default: + return ''; + } +}; diff --git a/src/lib/utils/skin.ts b/src/lib/utils/skin.ts new file mode 100644 index 00000000..a5368367 --- /dev/null +++ b/src/lib/utils/skin.ts @@ -0,0 +1,107 @@ +import {Asset} from "../types/steam"; +import {ItemInfo} from "../bridge/handlers/fetch_inspect_info"; +import {getDopplerPhase, hasDopplerPhase} from "./dopplers"; +import {html, TemplateResult} from "lit"; + +export function rangeFromWear(wear: number): [number, number]|null { + const wearRanges: [number, number][] = [ + [0.0, 0.07], + [0.07, 0.15], + [0.15, 0.38], + [0.38, 0.45], + [0.45, 1.0] + ]; + + for (const range of wearRanges) { + if (wear > range[0] && wear <= range[1]) { + return range; + } + } + + return null; +} + +export function getLowestRank(info: ItemInfo): number|undefined { + if (!info.low_rank && !info.high_rank) { + // Item has no rank to return + return; + } + + return (info.low_rank || 1001) < (info.high_rank || 1001) ? + info.low_rank : info.high_rank; +} + +export function parseRank(info: ItemInfo): {order: OrderType, rank: number}|undefined { + const rank = getLowestRank(info); + if (rank && rank <= 1000) { + return { + order: rank === info.low_rank ? OrderType.LOW_RANK : OrderType.HIGH_RANK, + rank + } + } +} + +export function formatFloatWithRank(info: ItemInfo, precisionDigits = 14): string { + let r = info.floatvalue.toFixed(precisionDigits); + + const ranked = parseRank(info); + if (ranked) { + r += ` (#${ranked.rank})`; + } + + return r; +} + +export function formatSeed(info: ItemInfo): string { + let r = info.paintseed.toString(); + + if (hasDopplerPhase(info.paintindex)) { + r += ` (${getDopplerPhase(info.paintindex)})`; + } + + return r; +} + +enum OrderType { + LOW_RANK = 1, + HIGH_RANK = -1 +} + +/** + * Gets formatted link for floatdb for the specified item type and order + * @param info item properties dict + * @param order 1 for low float, -1 for high float ordering + */ +function getFloatDbLink(info: ItemInfo, order: OrderType): string { + function getFloatDbCategory(item: ItemInfo): number { + if (item.full_item_name!.includes('StatTrak')) { + return 2; + } else if (item.full_item_name!.includes('Souvenir')) { + return 3; + } else { + // "Normal" + return 1; + } + } + + return `https://csgofloat.com/db?defIndex=${info.defindex}&paintIndex=${info.paintindex}&order=${order}&category=${getFloatDbCategory(info)}`; +} + +export function renderClickableRank(info: ItemInfo): TemplateResult<1> { + const parsedRank = parseRank(info); + if (!parsedRank) { + return html``; + } + + return html` + + (Rank #${parsedRank.rank}) + `; +} + +export function isSkin(asset: Asset): boolean { + return !!asset.tags?.find( + a => a.category === 'Weapon' + || (a.category === 'Type' && a.internal_name === 'Type_Hands')); +} diff --git a/src/lib/utils/snips.ts b/src/lib/utils/snips.ts new file mode 100644 index 00000000..aaff915a --- /dev/null +++ b/src/lib/utils/snips.ts @@ -0,0 +1,3 @@ +export function inPageContext() { + return !chrome.extension; +} diff --git a/model_frame.html b/src/model_frame.html similarity index 100% rename from model_frame.html rename to src/model_frame.html diff --git a/model_frame.js b/src/model_frame.js similarity index 100% rename from model_frame.js rename to src/model_frame.js diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..60062012 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "strict": true, + "module": "es6", + "target": "es6", + "esModuleInterop": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "dist/js", + "noEmitOnError": true, + "typeRoots": [ "node_modules/@types", "./src/lib/types" ], + "experimentalDecorators": true, + "useDefineForClassFields": false, + "moduleResolution": "Node", + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 00000000..8fc3c10a --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,91 @@ +const path = require('path'); +const glob = require('glob'); +const CopyPlugin = require('copy-webpack-plugin'); +const MiniCssExtractPlugin = require("mini-css-extract-plugin"); +const webpack = require('webpack'); + +const CHROME_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmf6qYyrFypvvB0klM/hHhIdGO0bAnUT6ZK4fLqGl8/l5OWze2leeQkC/Nf0HOTQ50d2sdgXFuQDfHsMF+HQ5pUVIUT/25MzEXZWGwqi+JAxEX9Q/yGFFN53nI4m7mGNzCQ0TDUS3IrJsFfQBMEdv/fAwnhEitF/Ko9qn8/KYDzZqIjujwXKKeqlx+UXIvkgblI44RT9evwiqp+/WjZZ/YQzLa9tFhdz0Ct3Qvhn/03YrLAXa+yxXKpLAjQJ9DpYJoa++bJwluffinxKQUX0tm5dzFRSRKFCG92hKHnQHcQFUnBlDKF4LS0KQhgelyiTxN4GmKX7I1xQS/B1TByLL2wIDAQAB"; + +function getPathEntries(path) { + return glob.sync(path).reduce((acc, e) => { + if (!e.includes('node_modules')) { + // Remove extension + acc[e.replace(/\.[^/.]+$/, '')] = e; + } + + return acc; + }, {}); +} + +module.exports = (env) => { + const mode = env.mode || 'development'; + + return { + mode: "none", + entry: Object.assign( + getPathEntries('./src/lib/page_scripts/*.ts'), + getPathEntries('./src/lib/types/*.d.ts'), + getPathEntries('./src/background.ts'), + getPathEntries('./src/**/*.js')), + output: { + path: path.join(__dirname, "dist"), + filename: "[name].js", + }, + resolve: { + extensions: [".ts", ".js", ".html"], + }, + module: { + rules: [ + { + test: /\.ts$/, + loader: "ts-loader", + exclude: /node_modules|\.d\.ts$/, + }, + { + test: /\.d\.ts$/, + loader: 'ignore-loader' + }, + { + test: new RegExp(`.(css)$`), + loader: 'file-loader', + options: { + name : '[name].[ext]' + }, + exclude: /node_modules/, + }, + ], + }, + plugins: [ + new MiniCssExtractPlugin(), + new webpack.SourceMapDevToolPlugin({}), + new CopyPlugin({ + patterns: [ + {from: "icons", to: "icons", context: "."}, + {from: "src/model_frame.html", to: "src/", context: "."}, + {from: "src/global.css", to: "src/", context: "."}, + { + from: 'manifest.json', + to: 'manifest.json', + transform(raw) { + if (mode !== 'development') { + return raw; + } + + // Ensure local dev version has same ID as PROD + const manifest = JSON.parse(raw.toString()); + manifest.key = CHROME_KEY; + + return JSON.stringify(manifest, null, 2); + }, + }, + ] + }), + ], + stats: { + errorDetails: true, + }, + optimization: { + usedExports: true + } + } +};