{"version":3,"file":"questionspinner.min.js","sources":["https:\/\/moodle.sonsbeekmedia.nl\/caie_39\/mod\/teachingtools\/teachingapp\/questionspinner\/amd\/src\/questionspinner.js"],"sourcesContent":["\/\/ This file is part of Moodle - http:\/\/moodle.org\/\n\/\/\n\/\/ Moodle is free software: you can redistribute it and\/or modify\n\/\/ it under the terms of the GNU General Public License as published by\n\/\/ the Free Software Foundation, either version 3 of the License, or\n\/\/ (at your option) any later version.\n\/\/\n\/\/ Moodle is distributed in the hope that it will be useful,\n\/\/ but WITHOUT ANY WARRANTY; without even the implied warranty of\n\/\/ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\/\/ GNU General Public License for more details.\n\/\/\n\/\/ You should have received a copy of the GNU General Public License\n\/\/ along with Moodle. If not, see .\n\n\/**\n * The simple drag and drop app.\n *\n * @module teachingapp_questionspinner\/questionspinner\n * @class questionspinner\n * @copyright 2023 Bas Brands\n * @author Bas Brands \n * @license http:\/\/www.gnu.org\/copyleft\/gpl.html GNU GPL v3 or later\n *\/\n\nimport GameEngineLoader from 'mod_teachingtools\/game_engine';\nimport StoreConfigElement from 'mod_teachingtools\/names\/store_config_element';\nimport * as Tone from 'mod_teachingtools\/Tone';\nimport AriaEnhance from 'mod_teachingtools\/ariaenhance';\nclass GameElement {\n\n \/**\n * Constructor\n *\n * @param {Array} names The names to be used in the game.\n * @param {DOMNode} rootElement The root element of the game.\n * @param {Array} questionCircleTexts The store config class.\n *\/\n constructor(names, rootElement, questionCircleTexts) {\n this.names = names;\n this.configurednames = [...names.names];\n this.rootElement = rootElement;\n this.drawZone = this.rootElement.querySelector('[data-region=\"draw-zone\"]');\n this.canvas = this.rootElement.querySelector('#canvas');\n this.questionsRegion = this.rootElement.querySelector('[data-region=\"questionspinner-questions\"]');\n this.colors = ['#ffadad', '#ffd6a5', '#fdffb6', '#caffbf', '#9bf6ff', '#a0c4ff', '#bdb2ff', '#ffc6ff'];\n this.width = 1040;\n this.height = 693;\n this.paintWidth = 1040;\n this.paintHeight = 693;\n this.angle = 0;\n this.questionCircleTexts = questionCircleTexts;\n this.spinnerRunning = false;\n this.currentTab = 0;\n this.numwheel = 0;\n this.winner = null;\n\n this.storeConfig = new StoreConfigElement(this.names, this.rootElement);\n this.strings = this.rootElement.querySelector('[data-region=\"strings\"]');\n this.stringKeep = this.strings.dataset.keep;\n this.stringRemove = this.strings.dataset.remove;\n this.stringReset = this.strings.dataset.reset;\n\n this.synth = this.synth();\n this.createQuestionBoxes();\n this.questionspinner(false);\n this.addEventListeners();\n\n }\n\n \/\/ Setup the audio.\n synth() {\n return new Tone.Synth({\n oscillator: {\n type: 'sine',\n modulationFrequency: 0.2\n },\n envelope: {\n attack: 0,\n decay: 0.1,\n sustain: 0,\n release: 0.1,\n }\n }).toDestination();\n }\n\n \/**\n * Update the game.\n * @param {Array} names The names to be used in the game.\n *\/\n async update(names) {\n this.configurednames = [...names];\n this.names.names = names;\n this.questionspinner(false);\n }\n\n \/**\n * Chunk text into smaller chunks.\n *\n * @param {String} text Input text\n * @param {Int} size Chunk size\n * @param {String} delimiter Chunk delimiter\n * @return {Array} Array of text chunks\n *\/\n textChunck(text, size, delimiter = null) {\n if (text === undefined) {\n return [];\n }\n if (text.length > size) {\n \/\/ Split the text into chunks of 15 characters.\n const chunks = [];\n let chunk = '';\n for (let j = 0; j < text.length; j++) {\n chunk += text[j];\n if (chunk.length > size && (delimiter === null || text[j] === delimiter)) {\n chunks.push(chunk);\n chunk = '';\n }\n }\n if (chunk.length > 0) {\n chunks.push(chunk);\n }\n return chunks;\n } else {\n return [text];\n }\n }\n\n \/**\n * Shorten the names to fit the wheel.\n * @param {Array} names The names to be shortened.\n * @return {Array} The shortened names.\n *\/\n shortenNames(names) {\n names = names.map((section) => {\n if (section.length > 55) {\n return section.substring(0, 55) + '...';\n } else {\n return section;\n }\n });\n return names;\n }\n\n \/**\n * Run the spinner.\n *\/\n runSpinner = () => {\n if (this.spinnerRunning) {\n return;\n }\n if (this.names.names.length === 0) {\n return;\n }\n this.winner = Math.floor(Math.random() * this.names.names.length);\n this.questionspinner(this.winner);\n };\n\n \/**\n * Event listener for the spinner.\n *\/\n addEventListeners = () => {\n\n this.canvas.addEventListener('mousedown', () => {\n this.runSpinner();\n });\n\n this.canvas.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') {\n this.runSpinner();\n }\n });\n\n \/\/ On focus of the canvas, add a focus class to the drawzone.\n this.canvas.addEventListener('focus', () => {\n this.createFocusOutline();\n });\n\n \/\/ On blur of the canvas, remove the focus class from the drawzone.\n this.canvas.addEventListener('blur', () => {\n this.removeFocusOutline();\n });\n\n window.addEventListener('resize', () => {\n this.questionspinner(false);\n });\n\n const closeButton = this.rootElement.querySelector('[data-dismiss=\"configurator\"]');\n closeButton.addEventListener('click', () => {\n this.questionspinner(false);\n });\n\n document.addEventListener('AppFullscreenChange', () => {\n this.questionspinner(false);\n });\n\n \/\/ Listen for questioncircle changes.\n this.rootElement.addEventListener('update', () => {\n this.createQuestionBoxes();\n });\n\n AriaEnhance.addTabListener(this.rootElement);\n };\n\n boop = async() =>{\n if (this.rootElement.dataset.audiostarted === 'false') {\n await Tone.start();\n this.rootElement.dataset.audiostarted = 'true';\n }\n this.now = Tone.now();\n if (this.now === undefined) {\n return;\n }\n\n this.synth.triggerAttackRelease(400, '4n', this.now);\n };\n\n \/**\n * Create the full wheel.\n * @param {Int} winner The winner.\n *\/\n questionspinner = (winner) => {\n let canvas = document.getElementById(\"canvas\");\n let sections = this.shortenNames(this.names.names);\n\n if (sections.length === 0) {\n return;\n }\n \/\/ Reset all the drawing elements.\n let wheels = null;\n let frame = null;\n let currentSelected = null;\n\n \/\/ Resize the canvas to fit the screen.\n let paintratio = this.width \/ this.height;\n this.paintWidth = this.rootElement.clientWidth;\n this.paintHeight = this.paintWidth \/ paintratio;\n\n if (this.rootElement.classList.contains('fullscreen-app')) {\n this.paintWidth = this.rootElement.clientWidth;\n this.paintHeight = this.rootElement.clientHeight - 100;\n }\n\n canvas.width = this.paintWidth;\n canvas.height = this.paintHeight;\n\n const repaint = (angle) => {\n let r = Math.min(this.paintWidth, this.paintHeight) \/ 2.25 || 0;\n if (wheels === null) {\n wheels = this.createWheels(sections, r);\n }\n if (frame === null) {\n frame = this.createFrame(r);\n }\n\n \/\/ Middle of the canvas X and Y.\n let cx = this.paintWidth \/ 2;\n let cy = this.paintHeight \/ 2;\n\n let ctx = canvas.getContext(\"2d\");\n \/\/ Find which of the sections should be highlighted at the given angle.\n let selected = (Math.floor((-0.2 - angle) * sections.length \/ (2 * Math.PI))\n % sections.length);\n if (selected < 0) {\n selected += sections.length;\n }\n if (currentSelected !== selected && this.spinnerRunning) {\n this.boop();\n currentSelected = selected;\n }\n ctx.save();\n \/\/ Clear the canvas.\n ctx.beginPath();\n ctx.clearRect(0, 0, this.paintWidth, this.paintHeight);\n ctx.fill();\n ctx.save();\n ctx.translate(cx, cy);\n ctx.rotate(angle);\n ctx.translate(-wheels[selected].width \/ 2, -wheels[selected].height \/ 2);\n ctx.drawImage(wheels[selected], 0, 0);\n ctx.restore();\n ctx.drawImage(frame, cx - frame.width \/ 2, cy - frame.height \/ 2);\n ctx.restore();\n };\n\n let angle = this.angle;\n this.spinnerRunning = false;\n const spinTo = (winner, duration) => {\n let finalAngle = (-0.2) - (0.5 + winner) * 2 * Math.PI \/ this.names.names.length;\n let startAngle = angle - Math.floor(angle \/ (2 * Math.PI)) * 2 * Math.PI - 5 * 2 * Math.PI;\n let start = performance.now();\n const frame = () => {\n let now = performance.now();\n \/\/ Get numeric value of now without decimal places.\n let t = Math.min(1, (now - start) \/ duration);\n t = 3 * t * t - 2 * t * t * t; \/\/ Ease in out.\n angle = startAngle + t * (finalAngle - startAngle);\n repaint(angle, false);\n if (t < 1) {\n requestAnimationFrame(frame);\n } else {\n this.spinnerRunning = false;\n this.angle = angle;\n repaint(angle, winner);\n this.createWinnerPopup(winner);\n }\n };\n requestAnimationFrame(frame);\n this.spinnerRunning = true;\n };\n\n let csz = null;\n\n let sz = this.paintWidth + \"\/\" + this.paintHeight;\n if (csz !== sz) {\n csz = sz;\n wheels = frame = null;\n repaint(angle, false);\n }\n\n if (winner !== false) {\n spinTo(winner, 5000);\n }\n };\n\n \/**\n * Create the fields with the names in the spinner.\n * @param {Array} sections The names to add to the spinner.\n * @param {Number} r The radius of the spinner.\n *\n * @return {Array} Of Canvas elements.\n *\/\n createWheels = (sections, r) => {\n \/\/ Truncate each section name to 15 characters, and add an ellipsis if it's longer.\n sections = sections.map((section) => {\n if (section.length > 15) {\n return section.substring(0, 20) + '...';\n } else {\n return section;\n }\n });\n const wheels = [];\n for (let selected = 0; selected < sections.length; selected++) {\n let c = document.createElement(\"canvas\");\n c.width = c.height = 2 * r + 10;\n let ctx = c.getContext(\"2d\");\n let cx = 5 + r;\n let cy = 5 + r;\n let g = ctx.createRadialGradient(cx, cy, 0, cx, cy, r);\n g.addColorStop(0, \"rgba(0,0,0,0)\");\n g.addColorStop(1, \"rgba(0,0,0,0.1)\");\n for (let i = 0; i < sections.length; i++) {\n let a0 = 2 * Math.PI * i \/ sections.length;\n let a1 = a0 + 2 * Math.PI \/ (i == 0 ? 1 : sections.length);\n let a = 2 * Math.PI * (i + 0.5) \/ sections.length;\n ctx.beginPath();\n ctx.moveTo(cx, cy);\n ctx.arc(cx, cy, r, a0, a1, false);\n let colorindex = i % this.colors.length;\n ctx.fillStyle = this.colors[colorindex];\n ctx.fill();\n ctx.fillStyle = g;\n ctx.fill();\n ctx.save();\n if (i == selected) {\n ctx.fillStyle = \"#2b4eff\";\n ctx.shadowBlur = r \/ 20;\n } else {\n ctx.fillStyle = \"#333\";\n ctx.shadowBlur = r \/ 50;\n }\n let fontSize = r \/ 8;\n if (sections.length > 8) {\n fontSize = r \/ sections.length;\n }\n ctx.font = \"bold \" + fontSize + \"px 'Patrick hand'\";\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.translate(cx, cy);\n ctx.rotate(a);\n\n const lines = this.textChunck(sections[i], 5, ' ');\n \/\/ Draw each chunk of text.\n for (let j = 0; j < lines.length; j++) {\n ctx.font = \"bold \" + (fontSize - (lines.length * 2)) + \"px 'Patrick hand'\";\n ctx.fillText(lines[j], r * 0.63, (j - (lines.length - 1) \/ 2) * fontSize * 1.2);\n }\n ctx.restore();\n }\n wheels.push(c);\n }\n return wheels;\n };\n\n \/**\n * Create the frame around the spinner.\n * @param {Number} r The radius of the spinner.\n * @return {Canvas} The frame.\n *\/\n createFrame = (r) => {\n const frame = document.createElement(\"canvas\");\n frame.width = frame.height = 10 + 2 * r * 1.25 || 0;\n let ctx = frame.getContext(\"2d\");\n\n let cx = frame.width \/ 2;\n let cy = frame.height \/ 2;\n\n \/\/ Wheel shadow.\n ctx.beginPath();\n ctx.shadowOffsetX = r \/ 80;\n ctx.shadowOffsetY = r \/ 80;\n ctx.shadowBlur = r \/ 40;\n ctx.shadowColor = \"rgba(5,0,0,0.5)\";\n \/\/ Centre circle and edge.\n ctx.beginPath();\n ctx.arc(cx, cy, r * 1.025, 0, 2 * Math.PI, true);\n ctx.arc(cx, cy, r * 0.975, 0, 2 * Math.PI, false);\n ctx.fillStyle = \"#1c2863\";\n ctx.fill();\n\n ctx.shadowOffsetX = r \/ 40;\n ctx.shadowOffsetY = r \/ 40;\n let g = ctx.createRadialGradient(cx - r \/ 7, cy - r \/ 7, 0, cx, cy, r \/ 3);\n g.addColorStop(0, \"#FFF\");\n g.addColorStop(0.2, \"#7bb3fc\");\n g.addColorStop(1, \"#0a3a77\");\n ctx.fillStyle = g;\n ctx.beginPath();\n ctx.arc(cx, cy, r \/ 4.5, 0, 2 * Math.PI, false);\n ctx.fill();\n\n \/\/ Pointer indicating the selected section.\n ctx.translate(cx, cy);\n ctx.rotate(Math.PI - 0.2); \/\/ Position\n \/\/ Draw the pointer.\n ctx.beginPath();\n ctx.moveTo(-r * 1.1, -r * 0.05);\n ctx.lineTo(-r * 0.9, 0);\n ctx.lineTo(-r * 1.1, r * 0.05);\n ctx.fillStyle = \"#F44\";\n ctx.fill();\n return frame;\n };\n\n \/**\n * Create the question circles.\n *\/\n createQuestionBoxes = () => {\n this.questionsRegion.innerHTML = '';\n const texts = this.questionCircleTexts.getTexts();\n\n const createBox = (text, id) => {\n let box = document.createElement(\"div\");\n box.classList.add('question-box', 'question-box-' + id);\n box.setAttribute('tabindex', '0');\n let p = document.createElement(\"p\");\n p.textContent = text;\n box.appendChild(p);\n return box;\n };\n\n for (let i = 0; i < 4; i++) {\n let box = createBox(texts[i], i);\n this.questionsRegion.appendChild(box);\n }\n };\n\n \/**\n * Create a focus outline for the questionspinner.\n *\/\n createFocusOutline = () => {\n let focusOutline = document.createElement(\"div\");\n focusOutline.classList.add('focus-outline');\n this.drawZone.appendChild(focusOutline);\n };\n\n \/**\n * Remove the focus outline.\n *\/\n removeFocusOutline = () => {\n let focusOutline = this.drawZone.querySelector('.focus-outline');\n if (focusOutline) {\n focusOutline.remove();\n }\n };\n\n \/**\n * Create the winner popup.\n * @param {Int} winner The winner.\n *\/\n createWinnerPopup(winner) {\n let winnerName = this.names.names[winner];\n\n \/\/ Create the popup container\n const popUpContainer = document.createElement(\"div\");\n popUpContainer.classList.add('popup-container');\n\n \/\/ Create the popup\n const popup = document.createElement(\"div\");\n popup.classList.add('popup');\n const popupInner = document.createElement(\"div\");\n popupInner.classList.add('popup-inner');\n popup.appendChild(popupInner);\n\n \/\/ Add the winner text\n const winnerText = document.createElement(\"div\");\n winnerText.classList.add('winner-text');\n winnerText.textContent = winnerName;\n AriaEnhance.updateAriaLive(this.rootElement, winnerName, 'winner', 'teachingapp_questionspinner');\n popupInner.appendChild(winnerText);\n\n const buttons = document.createElement(\"div\");\n buttons.classList.add('buttons');\n \/\/ Add the \"Keep\" button\n const keepButton = document.createElement(\"button\");\n if (this.names.names.length > 1) {\n keepButton.classList.add('btn', 'actionbtn');\n keepButton.textContent = this.stringKeep;\n keepButton.addEventListener('click', (e) => {\n this.removePopup();\n this.questionspinner(false);\n e.preventDefault();\n });\n buttons.appendChild(keepButton);\n\n \/\/ Add the \"Remove\" button\n const removeButton = document.createElement(\"button\");\n removeButton.classList.add('btn', 'actionbtn');\n removeButton.textContent = this.stringRemove;\n removeButton.addEventListener('click', (e) => {\n \/\/ Remove the winner from .names.\n this.names.names.splice(this.winner, 1);\n \/\/ Redraw the spinner\n this.removePopup();\n this.questionspinner(false);\n e.preventDefault();\n });\n buttons.appendChild(removeButton);\n } else {\n const removeButton = document.createElement(\"button\");\n removeButton.classList.add('btn', 'actionbtn');\n removeButton.textContent = this.stringReset;\n removeButton.addEventListener('click', (e) => {\n this.removePopup();\n this.reset();\n e.preventDefault();\n });\n buttons.appendChild(removeButton);\n }\n popupInner.appendChild(buttons);\n\n \/\/ Add the popup to the popup container\n popUpContainer.appendChild(popup);\n\n \/\/ Add the popup container to the root element\n this.drawZone.appendChild(popUpContainer);\n setTimeout(() => {\n popUpContainer.classList.add('show');\n popup.classList.add('show');\n \/\/ Focus on the first button.\n buttons.querySelector('button').focus();\n }, 300);\n }\n\n \/**\n * Remove the popup.\n *\/\n removePopup() {\n const popupContainer = this.rootElement.querySelector('.popup-container');\n if (popupContainer) {\n popupContainer.remove();\n }\n this.canvas.focus();\n }\n\n \/**\n * Reset the spinner.\n *\/\n reset() {\n this.names.names = this.configurednames;\n this.questionspinner(false);\n }\n}\n\nclass QuestionCircleTexts {\n \/**\n * Constructor.\n * @param {Object} names\n * @param {DOMNode} rootElement\n * @param {Class} storeConfig\n *\/\n constructor(names, rootElement, storeConfig) {\n this.names = names;\n this.rootElement = rootElement;\n this.storeConfig = storeConfig;\n this.textAreas = this.rootElement.querySelectorAll('[data-region=\"questionspinner-question\"]');\n }\n\n \/**\n * Set the inital configuration.\n *\/\n setConfig = async() => {\n let configuredQuestions = await this.storeConfig.getCustomData('questioncircles');\n if (!configuredQuestions) {\n configuredQuestions = [];\n }\n this.textAreas.forEach((textArea) => {\n const questionnum = textArea.dataset.question;\n if (configuredQuestions[questionnum]) {\n textArea.value = configuredQuestions[questionnum];\n } else {\n textArea.value = '';\n }\n });\n };\n\n \/**\n * Add event listeners.\n * @return {Void}\n *\/\n addEventListeners = async() => {\n await this.setConfig();\n let configuredQuestions = await this.storeConfig.getCustomData('questioncircles');\n if (!configuredQuestions) {\n configuredQuestions = [];\n }\n const texts = [];\n this.textAreas.forEach((textArea) => {\n const questionnum = textArea.dataset.question;\n const text = textArea.value;\n const placeHolder = textArea.getAttribute('placeholder');\n if (text.length > 0) {\n texts.push(text);\n } else {\n texts.push(placeHolder);\n }\n textArea.addEventListener('change', () => {\n configuredQuestions[questionnum] = textArea.value;\n this.storeConfig.setCustomData('questioncircles', configuredQuestions);\n \/\/ Fire an event to update the game.\n const event = new CustomEvent('update', {\n detail: {\n names: texts\n }\n });\n this.rootElement.dispatchEvent(event);\n });\n });\n\n \/\/ Watch for the \"showremove\" checkbox to be checked.\n const showRemove = this.rootElement.querySelector('[data-region=\"questionspinner-showremove\"]');\n const showRemoveValue = await this.storeConfig.getCustomData('showremove');\n if (showRemoveValue === 'yes') {\n showRemove.checked = true;\n this.rootElement.dataset.showremove = true;\n } else {\n showRemove.checked = false;\n this.rootElement.dataset.showremove = false;\n }\n showRemove.addEventListener('change', () => {\n if (showRemove.checked) {\n this.storeConfig.setCustomData('showremove', 'yes');\n this.rootElement.dataset.showremove = \"true\";\n } else {\n this.storeConfig.setCustomData('showremove', 'no');\n this.rootElement.dataset.showremove = \"false\";\n }\n });\n return texts;\n };\n\n \/**\n * Get the texts.\n * @return {Array}\n *\/\n getTexts = () => {\n const textAreas = this.rootElement.querySelectorAll('[data-region=\"questionspinner-question\"]');\n const texts = [];\n textAreas.forEach((textArea) => {\n const text = textArea.value;\n const placeHolder = textArea.getAttribute('placeholder');\n if (text.length > 0) {\n texts.push(text);\n } else {\n texts.push(placeHolder);\n }\n });\n return texts;\n };\n}\n\nconst init = async(cmid) => {\n const GE = new GameEngineLoader('questionspinner', cmid);\n await GE.load();\n const questionCircleTexts = new QuestionCircleTexts(GE.names, GE.rootElement, GE.storeConfig);\n await questionCircleTexts.addEventListeners();\n const game = new GameElement(GE.names, GE.rootElement, questionCircleTexts);\n GE.run(game);\n};\n\nexport default {\n init: init,\n GameElement,\n QuestionCircleTexts\n};\n"],"names":["GameElement","constructor","names","rootElement","questionCircleTexts","this","spinnerRunning","length","winner","Math","floor","random","questionspinner","canvas","addEventListener","runSpinner","e","key","createFocusOutline","removeFocusOutline","window","querySelector","document","createQuestionBoxes","addTabListener","async","dataset","audiostarted","Tone","start","now","undefined","synth","triggerAttackRelease","getElementById","sections","shortenNames","wheels","frame","currentSelected","paintratio","width","height","paintWidth","clientWidth","paintHeight","classList","contains","clientHeight","repaint","angle","r","min","createWheels","createFrame","cx","cy","ctx","getContext","selected","PI","boop","save","beginPath","clearRect","fill","translate","rotate","drawImage","restore","spinTo","duration","finalAngle","startAngle","performance","t","requestAnimationFrame","createWinnerPopup","csz","sz","map","section","substring","c","createElement","g","createRadialGradient","addColorStop","i","a0","a1","a","moveTo","arc","colorindex","colors","fillStyle","shadowBlur","fontSize","font","textAlign","textBaseline","lines","textChunck","j","fillText","push","shadowOffsetX","shadowOffsetY","shadowColor","lineTo","questionsRegion","innerHTML","texts","getTexts","createBox","text","id","box","add","setAttribute","p","textContent","appendChild","focusOutline","drawZone","remove","configurednames","currentTab","numwheel","storeConfig","StoreConfigElement","strings","stringKeep","keep","stringRemove","stringReset","reset","addEventListeners","Synth","oscillator","type","modulationFrequency","envelope","attack","decay","sustain","release","toDestination","size","delimiter","chunks","chunk","winnerName","popUpContainer","popup","popupInner","winnerText","updateAriaLive","buttons","keepButton","removePopup","preventDefault","removeButton","splice","setTimeout","focus","popupContainer","QuestionCircleTexts","configuredQuestions","getCustomData","textAreas","forEach","textArea","questionnum","question","value","setConfig","placeHolder","getAttribute","setCustomData","event","CustomEvent","detail","dispatchEvent","showRemove","checked","showremove","querySelectorAll","init","GE","GameEngineLoader","cmid","load","game","run"],"mappings":"irDA6BMA,YASFC,YAAYC,MAAOC,YAAaC,wDA6GnB,KACLC,KAAKC,gBAGuB,IAA5BD,KAAKH,MAAMA,MAAMK,cAGhBC,OAASC,KAAKC,MAAMD,KAAKE,SAAWN,KAAKH,MAAMA,MAAMK,aACrDK,gBAAgBP,KAAKG,sDAMV,UAEXK,OAAOC,iBAAiB,aAAa,UACjCC,qBAGJF,OAAOC,iBAAiB,WAAYE,IACvB,UAAVA,EAAEC,UACGF,qBAKRF,OAAOC,iBAAiB,SAAS,UAC7BI,6BAIJL,OAAOC,iBAAiB,QAAQ,UAC5BK,wBAGTC,OAAON,iBAAiB,UAAU,UACzBF,iBAAgB,MAGLP,KAAKF,YAAYkB,cAAc,iCACvCP,iBAAiB,SAAS,UAC7BF,iBAAgB,MAGzBU,SAASR,iBAAiB,uBAAuB,UACxCF,iBAAgB,WAIpBT,YAAYW,iBAAiB,UAAU,UACnCS,8CAGGC,eAAenB,KAAKF,6CAG7BsB,UAC2C,UAA1CpB,KAAKF,YAAYuB,QAAQC,qBACnBC,KAAKC,aACN1B,YAAYuB,QAAQC,aAAe,aAEvCG,IAAMF,KAAKE,WACCC,IAAb1B,KAAKyB,UAIJE,MAAMC,qBAAqB,IAAK,KAAM5B,KAAKyB,gDAOjCtB,aACXK,OAASS,SAASY,eAAe,UACjCC,SAAW9B,KAAK+B,aAAa\/B,KAAKH,MAAMA,UAEpB,IAApBiC,SAAS5B,kBAIT8B,OAAS,KACTC,MAAQ,KACRC,gBAAkB,KAGlBC,WAAanC,KAAKoC,MAAQpC,KAAKqC,YAC9BC,WAAatC,KAAKF,YAAYyC,iBAC9BC,YAAcxC,KAAKsC,WAAaH,WAEjCnC,KAAKF,YAAY2C,UAAUC,SAAS,yBAC\/BJ,WAAatC,KAAKF,YAAYyC,iBAC9BC,YAAcxC,KAAKF,YAAY6C,aAAe,KAGvDnC,OAAO4B,MAAQpC,KAAKsC,WACpB9B,OAAO6B,OAASrC,KAAKwC,kBAEfI,QAAWC,YACTC,EAAI1C,KAAK2C,IAAI\/C,KAAKsC,WAAYtC,KAAKwC,aAAe,MAAQ,EAC\/C,OAAXR,SACAA,OAAShC,KAAKgD,aAAalB,SAAUgB,IAE3B,OAAVb,QACAA,MAAQjC,KAAKiD,YAAYH,QAIzBI,GAAKlD,KAAKsC,WAAa,EACvBa,GAAKnD,KAAKwC,YAAc,EAExBY,IAAM5C,OAAO6C,WAAW,MAExBC,SAAYlD,KAAKC,QAAQ,GAAMwC,OAASf,SAAS5B,QAAU,EAAIE,KAAKmD,KACtDzB,SAAS5B,OACvBoD,SAAW,IACXA,UAAYxB,SAAS5B,QAErBgC,kBAAoBoB,UAAYtD,KAAKC,sBAChCuD,OACLtB,gBAAkBoB,UAEtBF,IAAIK,OAEJL,IAAIM,YACJN,IAAIO,UAAU,EAAG,EAAG3D,KAAKsC,WAAYtC,KAAKwC,aAC1CY,IAAIQ,OACJR,IAAIK,OACJL,IAAIS,UAAUX,GAAIC,IAClBC,IAAIU,OAAOjB,OACXO,IAAIS,WAAW7B,OAAOsB,UAAUlB,MAAQ,GAAIJ,OAAOsB,UAAUjB,OAAS,GACtEe,IAAIW,UAAU\/B,OAAOsB,UAAW,EAAG,GACnCF,IAAIY,UACJZ,IAAIW,UAAU9B,MAAOiB,GAAKjB,MAAMG,MAAQ,EAAGe,GAAKlB,MAAMI,OAAS,GAC\/De,IAAIY,eAGJnB,MAAQ7C,KAAK6C,WACZ5C,gBAAiB,QAChBgE,OAAS,CAAC9D,OAAQ+D,gBAChBC,YAAe,GAAwB,GAAhB,GAAMhE,QAAcC,KAAKmD,GAAKvD,KAAKH,MAAMA,MAAMK,OACtEkE,WAAavB,MAA4C,EAApCzC,KAAKC,MAAMwC,OAAS,EAAIzC,KAAKmD,KAAWnD,KAAKmD,GAAK,GAAQnD,KAAKmD,GACpF\/B,MAAQ6C,YAAY5C,YAClBQ,MAAQ,SACNR,IAAM4C,YAAY5C,MAElB6C,EAAIlE,KAAK2C,IAAI,GAAItB,IAAMD,OAAS0C,UACpCI,EAAI,EAAIA,EAAIA,EAAI,EAAIA,EAAIA,EAAIA,EAC5BzB,MAAQuB,WAAaE,GAAKH,WAAaC,YACvCxB,QAAQC,OACJyB,EAAI,EACJC,sBAAsBtC,aAEjBhC,gBAAiB,OACjB4C,MAAQA,MACbD,QAAQC,YACH2B,kBAAkBrE,UAG\/BoE,sBAAsBtC,YACjBhC,gBAAiB,OAGtBwE,IAAM,KAENC,GAAK1E,KAAKsC,WAAa,IAAMtC,KAAKwC,YAClCiC,MAAQC,KACRD,IAAMC,GACN1C,OAASC,MAAQ,KACjBW,QAAQC,SAGG,IAAX1C,QACA8D,OAAO9D,OAAQ,6CAWR,CAAC2B,SAAUgB,KAEtBhB,SAAWA,SAAS6C,KAAKC,SACjBA,QAAQ1E,OAAS,GACV0E,QAAQC,UAAU,EAAG,IAAM,MAE3BD,gBAGT5C,OAAS,OACV,IAAIsB,SAAW,EAAGA,SAAWxB,SAAS5B,OAAQoD,WAAY,KACvDwB,EAAI7D,SAAS8D,cAAc,UAC\/BD,EAAE1C,MAAQ0C,EAAEzC,OAAS,EAAIS,EAAI,OACzBM,IAAM0B,EAAEzB,WAAW,MACnBH,GAAK,EAAIJ,EACTK,GAAK,EAAIL,EACTkC,EAAI5B,IAAI6B,qBAAqB\/B,GAAIC,GAAI,EAAGD,GAAIC,GAAIL,GACpDkC,EAAEE,aAAa,EAAG,iBAClBF,EAAEE,aAAa,EAAG,uBACb,IAAIC,EAAI,EAAGA,EAAIrD,SAAS5B,OAAQiF,IAAK,KAClCC,GAAK,EAAIhF,KAAKmD,GAAK4B,EAAIrD,SAAS5B,OAChCmF,GAAKD,GAAK,EAAIhF,KAAKmD,IAAW,GAAL4B,EAAS,EAAIrD,SAAS5B,QAC\/CoF,EAAI,EAAIlF,KAAKmD,IAAM4B,EAAI,IAAOrD,SAAS5B,OAC3CkD,IAAIM,YACJN,IAAImC,OAAOrC,GAAIC,IACfC,IAAIoC,IAAItC,GAAIC,GAAIL,EAAGsC,GAAIC,IAAI,OACvBI,WAAaN,EAAInF,KAAK0F,OAAOxF,OACjCkD,IAAIuC,UAAY3F,KAAK0F,OAAOD,YAC5BrC,IAAIQ,OACJR,IAAIuC,UAAYX,EAChB5B,IAAIQ,OACJR,IAAIK,OACA0B,GAAK7B,UACLF,IAAIuC,UAAY,UAChBvC,IAAIwC,WAAa9C,EAAI,KAErBM,IAAIuC,UAAY,OAChBvC,IAAIwC,WAAa9C,EAAI,QAErB+C,SAAW\/C,EAAI,EACfhB,SAAS5B,OAAS,IAClB2F,SAAW\/C,EAAIhB,SAAS5B,QAE5BkD,IAAI0C,KAAO,QAAUD,SAAW,oBAChCzC,IAAI2C,UAAY,SAChB3C,IAAI4C,aAAe,SACnB5C,IAAIS,UAAUX,GAAIC,IAClBC,IAAIU,OAAOwB,SAELW,MAAQjG,KAAKkG,WAAWpE,SAASqD,GAAI,EAAG,SAEzC,IAAIgB,EAAI,EAAGA,EAAIF,MAAM\/F,OAAQiG,IAC9B\/C,IAAI0C,KAAO,SAAWD,SAA2B,EAAfI,MAAM\/F,QAAe,oBACvDkD,IAAIgD,SAASH,MAAME,GAAQ,IAAJrD,GAAWqD,GAAKF,MAAM\/F,OAAS,GAAK,GAAK2F,SAAW,KAE\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\/Bd,MAAMA,MAAMkK,OAAO\/J,KAAKG,OAAQ,QAEhCyJ,mBACArJ,iBAAgB,GACrBI,EAAEkJ,oBAENH,QAAQpC,YAAYwC,kBACjB,OACGA,aAAe7I,SAAS8D,cAAc,UAC5C+E,aAAarH,UAAUyE,IAAI,MAAO,aAClC4C,aAAazC,YAAcrH,KAAKmI,YAChC2B,aAAarJ,iBAAiB,SAAUE,SAC\/BiJ,mBACAxB,QACLzH,EAAEkJ,oBAENH,QAAQpC,YAAYwC,cAExBP,WAAWjC,YAAYoC,SAGvBL,eAAe\/B,YAAYgC,YAGtB9B,SAASF,YAAY+B,gBAC1BW,YAAW,KACPX,eAAe5G,UAAUyE,IAAI,QAC7BoC,MAAM7G,UAAUyE,IAAI,QAEpBwC,QAAQ1I,cAAc,UAAUiJ,UACjC,KAMPL,oBACUM,eAAiBlK,KAAKF,YAAYkB,cAAc,oBAClDkJ,gBACAA,eAAezC,cAEdjH,OAAOyJ,QAMhB7B,aACSvI,MAAMA,MAAQG,KAAK0H,qBACnBnH,iBAAgB,UAIvB4J,oBAOFvK,YAAYC,MAAOC,YAAa+H,+CAUpBzG,cACJgJ,0BAA4BpK,KAAK6H,YAAYwC,cAAc,mBAC1DD,sBACDA,oBAAsB,SAErBE,UAAUC,SAASC,iBACdC,YAAcD,SAASnJ,QAAQqJ,SACjCN,oBAAoBK,aACpBD,SAASG,MAAQP,oBAAoBK,aAErCD,SAASG,MAAQ,mDASTvJ,gBACVpB,KAAK4K,gBACPR,0BAA4BpK,KAAK6H,YAAYwC,cAAc,mBAC1DD,sBACDA,oBAAsB,UAEpBxD,MAAQ,QACT0D,UAAUC,SAASC,iBACdC,YAAcD,SAASnJ,QAAQqJ,SAC\/B3D,KAAOyD,SAASG,MAChBE,YAAcL,SAASM,aAAa,eACtC\/D,KAAK7G,OAAS,EACd0G,MAAMP,KAAKU,MAEXH,MAAMP,KAAKwE,aAEfL,SAAS\/J,iBAAiB,UAAU,KAChC2J,oBAAoBK,aAAeD,SAASG,WACvC9C,YAAYkD,cAAc,kBAAmBX,2BAE5CY,MAAQ,IAAIC,YAAY,SAAU,CACpCC,OAAQ,CACJrL,MAAO+G,cAGV9G,YAAYqL,cAAcH,mBAKjCI,WAAapL,KAAKF,YAAYkB,cAAc,oDAE1B,cADMhB,KAAK6H,YAAYwC,cAAc,eAEzDe,WAAWC,SAAU,OAChBvL,YAAYuB,QAAQiK,YAAa,IAEtCF,WAAWC,SAAU,OAChBvL,YAAYuB,QAAQiK,YAAa,GAE1CF,WAAW3K,iBAAiB,UAAU,KAC9B2K,WAAWC,cACNxD,YAAYkD,cAAc,aAAc,YACxCjL,YAAYuB,QAAQiK,WAAa,cAEjCzD,YAAYkD,cAAc,aAAc,WACxCjL,YAAYuB,QAAQiK,WAAa,YAGvC1E,0CAOA,WACD0D,UAAYtK,KAAKF,YAAYyL,iBAAiB,4CAC9C3E,MAAQ,UACd0D,UAAUC,SAASC,iBACTzD,KAAOyD,SAASG,MAChBE,YAAcL,SAASM,aAAa,eACtC\/D,KAAK7G,OAAS,EACd0G,MAAMP,KAAKU,MAEXH,MAAMP,KAAKwE,gBAGZjE,cA\/FF\/G,MAAQA,WACRC,YAAcA,iBACd+H,YAAcA,iBACdyC,UAAYtK,KAAKF,YAAYyL,iBAAiB,0DAyG5C,CACXC,KAVSpK,MAAAA,aACHqK,GAAK,IAAIC,qBAAiB,kBAAmBC,YAC7CF,GAAGG,aACH7L,oBAAsB,IAAIoK,oBAAoBsB,GAAG5L,MAAO4L,GAAG3L,YAAa2L,GAAG5D,mBAC3E9H,oBAAoBsI,0BACpBwD,KAAO,IAAIlM,YAAY8L,GAAG5L,MAAO4L,GAAG3L,YAAaC,qBACvD0L,GAAGK,IAAID,OAKPlM,YAAAA,YACAwK,oBAAAA"}