From 7d29638552bf7976f5d00f33e93d6a0122b07f7b Mon Sep 17 00:00:00 2001 From: Mattias Bodlund Date: Tue, 27 May 2025 15:03:12 +0200 Subject: [PATCH] na --- Gemfile.lock | 4 +- app/assets/stylesheets/application.css | 118 +++-- app/controllers/answers_controller.rb | 3 + app/controllers/questions_controller.rb | 31 +- app/helpers/questions_helper.rb | 16 +- app/javascript/application.js | 444 ++++++++++++++++++ app/javascript/image_controller.js | 23 + app/javascript/locale_controller.js | 20 +- app/models/quiz_result.rb | 15 + app/views/languages/_intro.html.erb | 21 +- app/views/languages/index.html.erb | 2 +- app/views/layouts/application.html.erb | 8 +- app/views/players/new.html.erb | 27 +- app/views/questions/answer.html.erb | 36 +- app/views/questions/result.html.erb | 35 +- app/views/questions/shared_result.html.erb | 41 ++ app/views/questions/show.html.erb | 33 +- config/importmap.rb | 1 + config/locales/en.yml | 4 + config/routes.rb | 2 +- .../20250527114853_create_quiz_results.rb | 15 + db/schema.rb | 13 +- public/400.html | 5 +- public/404.html | 3 +- public/406-unsupported-browser.html | 3 +- public/422.html | 3 +- public/500.html | 3 +- test/fixtures/quiz_results.yml | 11 + test/models/quiz_result_test.rb | 7 + vendor/javascript/gsap/.DS_Store | Bin 0 -> 6148 bytes vendor/javascript/gsap/CSSRulePlugin.min.js | 11 + .../javascript/gsap/CSSRulePlugin.min.js.map | 1 + vendor/javascript/gsap/CustomBounce.min.js | 11 + .../javascript/gsap/CustomBounce.min.js.map | 1 + vendor/javascript/gsap/CustomEase.min.js | 11 + vendor/javascript/gsap/CustomEase.min.js.map | 1 + vendor/javascript/gsap/CustomWiggle.min.js | 11 + .../javascript/gsap/CustomWiggle.min.js.map | 1 + vendor/javascript/gsap/Draggable.min.js | 11 + vendor/javascript/gsap/Draggable.min.js.map | 1 + vendor/javascript/gsap/DrawSVGPlugin.min.js | 11 + .../javascript/gsap/DrawSVGPlugin.min.js.map | 1 + vendor/javascript/gsap/EasePack.min.js | 11 + vendor/javascript/gsap/EasePack.min.js.map | 1 + vendor/javascript/gsap/EaselPlugin.min.js | 11 + vendor/javascript/gsap/EaselPlugin.min.js.map | 1 + vendor/javascript/gsap/Flip.min.js | 11 + vendor/javascript/gsap/Flip.min.js.map | 1 + vendor/javascript/gsap/GSDevTools.min.js | 11 + vendor/javascript/gsap/GSDevTools.min.js.map | 1 + vendor/javascript/gsap/InertiaPlugin.min.js | 11 + .../javascript/gsap/InertiaPlugin.min.js.map | 1 + vendor/javascript/gsap/MorphSVGPlugin.min.js | 11 + .../javascript/gsap/MorphSVGPlugin.min.js.map | 1 + .../javascript/gsap/MotionPathHelper.min.js | 11 + .../gsap/MotionPathHelper.min.js.map | 1 + .../javascript/gsap/MotionPathPlugin.min.js | 11 + .../gsap/MotionPathPlugin.min.js.map | 1 + vendor/javascript/gsap/Observer.min.js | 11 + vendor/javascript/gsap/Observer.min.js.map | 1 + vendor/javascript/gsap/Physics2DPlugin.min.js | 11 + .../gsap/Physics2DPlugin.min.js.map | 1 + .../javascript/gsap/PhysicsPropsPlugin.min.js | 11 + .../gsap/PhysicsPropsPlugin.min.js.map | 1 + vendor/javascript/gsap/PixiPlugin.min.js | 11 + vendor/javascript/gsap/PixiPlugin.min.js.map | 1 + .../javascript/gsap/ScrambleTextPlugin.min.js | 11 + .../gsap/ScrambleTextPlugin.min.js.map | 1 + vendor/javascript/gsap/ScrollSmoother.min.js | 11 + .../javascript/gsap/ScrollSmoother.min.js.map | 1 + vendor/javascript/gsap/ScrollToPlugin.min.js | 11 + .../javascript/gsap/ScrollToPlugin.min.js.map | 1 + vendor/javascript/gsap/ScrollTrigger.min.js | 11 + .../javascript/gsap/ScrollTrigger.min.js.map | 1 + vendor/javascript/gsap/SplitText.min.js | 11 + vendor/javascript/gsap/SplitText.min.js.map | 1 + vendor/javascript/gsap/TextPlugin.min.js | 11 + vendor/javascript/gsap/TextPlugin.min.js.map | 1 + vendor/javascript/gsap/gsap.min.js | 11 + vendor/javascript/gsap/gsap.min.js.map | 1 + 80 files changed, 1133 insertions(+), 114 deletions(-) create mode 100644 app/javascript/image_controller.js create mode 100644 app/models/quiz_result.rb create mode 100644 app/views/questions/shared_result.html.erb create mode 100644 db/migrate/20250527114853_create_quiz_results.rb create mode 100644 test/fixtures/quiz_results.yml create mode 100644 test/models/quiz_result_test.rb create mode 100644 vendor/javascript/gsap/.DS_Store create mode 100644 vendor/javascript/gsap/CSSRulePlugin.min.js create mode 100644 vendor/javascript/gsap/CSSRulePlugin.min.js.map create mode 100644 vendor/javascript/gsap/CustomBounce.min.js create mode 100644 vendor/javascript/gsap/CustomBounce.min.js.map create mode 100644 vendor/javascript/gsap/CustomEase.min.js create mode 100644 vendor/javascript/gsap/CustomEase.min.js.map create mode 100644 vendor/javascript/gsap/CustomWiggle.min.js create mode 100644 vendor/javascript/gsap/CustomWiggle.min.js.map create mode 100644 vendor/javascript/gsap/Draggable.min.js create mode 100644 vendor/javascript/gsap/Draggable.min.js.map create mode 100644 vendor/javascript/gsap/DrawSVGPlugin.min.js create mode 100644 vendor/javascript/gsap/DrawSVGPlugin.min.js.map create mode 100644 vendor/javascript/gsap/EasePack.min.js create mode 100644 vendor/javascript/gsap/EasePack.min.js.map create mode 100644 vendor/javascript/gsap/EaselPlugin.min.js create mode 100644 vendor/javascript/gsap/EaselPlugin.min.js.map create mode 100644 vendor/javascript/gsap/Flip.min.js create mode 100644 vendor/javascript/gsap/Flip.min.js.map create mode 100644 vendor/javascript/gsap/GSDevTools.min.js create mode 100644 vendor/javascript/gsap/GSDevTools.min.js.map create mode 100644 vendor/javascript/gsap/InertiaPlugin.min.js create mode 100644 vendor/javascript/gsap/InertiaPlugin.min.js.map create mode 100644 vendor/javascript/gsap/MorphSVGPlugin.min.js create mode 100644 vendor/javascript/gsap/MorphSVGPlugin.min.js.map create mode 100644 vendor/javascript/gsap/MotionPathHelper.min.js create mode 100644 vendor/javascript/gsap/MotionPathHelper.min.js.map create mode 100644 vendor/javascript/gsap/MotionPathPlugin.min.js create mode 100644 vendor/javascript/gsap/MotionPathPlugin.min.js.map create mode 100644 vendor/javascript/gsap/Observer.min.js create mode 100644 vendor/javascript/gsap/Observer.min.js.map create mode 100644 vendor/javascript/gsap/Physics2DPlugin.min.js create mode 100644 vendor/javascript/gsap/Physics2DPlugin.min.js.map create mode 100644 vendor/javascript/gsap/PhysicsPropsPlugin.min.js create mode 100644 vendor/javascript/gsap/PhysicsPropsPlugin.min.js.map create mode 100644 vendor/javascript/gsap/PixiPlugin.min.js create mode 100644 vendor/javascript/gsap/PixiPlugin.min.js.map create mode 100644 vendor/javascript/gsap/ScrambleTextPlugin.min.js create mode 100644 vendor/javascript/gsap/ScrambleTextPlugin.min.js.map create mode 100644 vendor/javascript/gsap/ScrollSmoother.min.js create mode 100644 vendor/javascript/gsap/ScrollSmoother.min.js.map create mode 100644 vendor/javascript/gsap/ScrollToPlugin.min.js create mode 100644 vendor/javascript/gsap/ScrollToPlugin.min.js.map create mode 100644 vendor/javascript/gsap/ScrollTrigger.min.js create mode 100644 vendor/javascript/gsap/ScrollTrigger.min.js.map create mode 100644 vendor/javascript/gsap/SplitText.min.js create mode 100644 vendor/javascript/gsap/SplitText.min.js.map create mode 100644 vendor/javascript/gsap/TextPlugin.min.js create mode 100644 vendor/javascript/gsap/TextPlugin.min.js.map create mode 100644 vendor/javascript/gsap/gsap.min.js create mode 100644 vendor/javascript/gsap/gsap.min.js.map diff --git a/Gemfile.lock b/Gemfile.lock index d7b59cd..f7b30cb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -130,7 +130,7 @@ GEM jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - json (2.12.0) + json (2.12.2) kaminari (1.2.2) activesupport (>= 4.1.0) kaminari-actionview (= 1.2.2) @@ -289,7 +289,7 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - websocket-driver (0.7.7) + websocket-driver (0.8.0) base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index 4b455b1..10cf3a2 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -92,10 +92,6 @@ --fs-3xl: 4.0rem; --fs-4xl: 9.0rem; - --flip-deg: 0deg; - --flip-scale: 1; - --flip-rotate: 0deg; - font: 10px/1.3 var(--ff-ikea); } @@ -107,7 +103,7 @@ body { display: flex; flex-direction: column; gap: 0; - min-height: 100svh; + min-height: 100dvh; -webkit-font-smoothing: antialiased; touch-action: manipulation; @@ -116,10 +112,6 @@ body { } } - - - - ul[class] { margin: 0 auto; padding: 0; @@ -210,7 +202,6 @@ footer { } } - .button__base { appearance: none; background-color: var(--clr-black); @@ -219,15 +210,16 @@ footer { font-size: var(--fs-base); font-family: var(--ff-ikea); font-weight: 700; - line-height: 1.2; - border-radius: 400px; + height: 4.4rem; + border-radius: 4.4rem; width: 100%; cursor: pointer; text-decoration: none; - display: inline-block; - text-align: center; + display: flex; + align-items: center; + justify-content: center; + padding: 0 1em; box-sizing: border-box; - padding: 0.62em 1em; } .button__base-light { @@ -258,6 +250,12 @@ main { width: 100%; height: 100%; object-fit: cover; + opacity: 0; + transition: opacity 0.4s ease-in-out; + + &.loaded { + opacity: 1; + } } } @@ -271,7 +269,7 @@ main { display: grid; grid-template-columns: 1; grid-template-rows: auto; - gap: 8px; + /*gap: 8px;*/ } .intro-content-container, @@ -279,8 +277,8 @@ main { display: grid; grid-template-columns: 1; grid-template-rows: auto; - gap: 8px; - + /*gap: 8px;*/ + width: 100%; } .intro-container, @@ -292,9 +290,10 @@ main { .intro-content-header, .intro-content-body { padding: 1.6rem 1.2rem; - border-radius: 1.2rem; + border-radius: 1.2rem; } + .intro-content-header { background-color: var(--clr-white); font-size: var(--fs-2xl); @@ -308,12 +307,6 @@ main { line-height: 1.4; } -.intro-content-container, -.question-container { - & > :last-child { - margin-top: 4px; - } -} .question-step { font-size: var(--fs-s); @@ -374,6 +367,10 @@ main { } } + & + .question-answer { + margin-top: 8px; + } + } .question-container { @@ -415,16 +412,30 @@ main { .answers-distribution { display: grid; grid-template-columns: 1fr 1fr; - gap: 0.8rem; + gap: 8px; + + + height: 45dvh; + max-height: 300px; & > div { + box-sizing: border-box; padding: 1.6rem 1.2rem; background-color: var(--clr-medium-yellow); border-radius: 1.2rem; color: var(--clr-dark-yellow); font-size: var(--fs-s); font-weight: 700; - line-height: 1.3; + line-height: 1.3; + + align-self: end; + min-height: 160px; + + display: flex; + flex-direction: column; + justify-content: space-between; + + transition: background-color 200ms ease-in-out, color 200ms ease-in-out; &.answered { background-color: var(--clr-white); @@ -432,9 +443,14 @@ main { } } + & .explanation { + display: none; + } + & .percentage { font-size: var(--fs-4xl); - line-height: 1; + line-height: 1; + opacity: 0; &::after { content: '%'; @@ -455,21 +471,21 @@ main { line-height: 1.1; margin: 0 0 0.4em 0; } -} - - + & div:first-child { + font-weight: 700; + } +} label.question-answer { display: flex; align-items: start; - min-height: 4.2em; + height: 10.8rem; cursor: pointer; padding: 1.6rem 1.2rem; border-radius: 1.6rem; - background-color: var(--clr-white); - + background-color: var(--clr-white); & input { flex-grow: 1; @@ -490,7 +506,41 @@ label { cursor: pointer; } +.animation-element { + box-sizing: border-box; + opacity: 0; + padding-top: 8px; + + &:last-child { + padding-top: 12px; + } + + & > * { + box-sizing: border-box; + } +} + +.typewriter-cursor::after { + content: '|'; + animation: typewriter-blink 1s infinite; + color: currentColor; + position: absolute; +} + +@keyframes typewriter-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +} +/*.cursor { + animation: blink 1000ms infinite; + color: var(--clr-black); + font-weight: 400; +} +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } +}*/ @media (min-width: 815px) { diff --git a/app/controllers/answers_controller.rb b/app/controllers/answers_controller.rb index 2398281..2388810 100644 --- a/app/controllers/answers_controller.rb +++ b/app/controllers/answers_controller.rb @@ -32,6 +32,9 @@ private params.require(:answer).permit( :value ) + + rescue ActionController::ParameterMissing + {} end diff --git a/app/controllers/questions_controller.rb b/app/controllers/questions_controller.rb index 8eabe2a..41688e1 100644 --- a/app/controllers/questions_controller.rb +++ b/app/controllers/questions_controller.rb @@ -33,7 +33,14 @@ class QuestionsController < ApplicationController current_player.update(score: planet_score) - redirect_to url_for(action: 'result') + quiz_result = QuizResult.create!( + player_name: current_player.name, + score: planet_score, + stats: current_player.stats, + locale: I18n.locale + ) + + redirect_to url_for(action: 'result', sid: quiz_result.share_id) end @@ -60,6 +67,28 @@ class QuestionsController < ApplicationController end + def shared_result + + @quiz_result = QuizResult.find_by!(share_id: params[:id]) + + people_score = questions.count - @quiz_result.score + score_diff = people_score - @quiz_result.score + + attachment_index = case + when score_diff >= 2 + 0 # People + when score_diff <= -2 + 1 # Planet + else + 2 # Balanced + end + + @result_attachment = result_node.attachments.offset(attachment_index).first + @stats_attachment = result_node.attachments.offset(3).first + + end + + end diff --git a/app/helpers/questions_helper.rb b/app/helpers/questions_helper.rb index 30dc0d2..4c638b9 100644 --- a/app/helpers/questions_helper.rb +++ b/app/helpers/questions_helper.rb @@ -1,5 +1,15 @@ module QuestionsHelper + def decorate_divs_for_typewriting_effect(html) + doc = Nokogiri::HTML.fragment(html, 'utf-8') + + doc.xpath('./*').each do |div| + div['class'] = 'typewriter-text' + end + + doc.to_html.html_safe + end + def image_orientation(file_attachment) return nil unless file_attachment&.attached? @@ -66,7 +76,8 @@ private srcset: rails_storage_proxy_path(landscape_asset.file.variant(resize_to_limit: [800, nil])) + " 800w, " + rails_storage_proxy_path(landscape_asset.file.variant(resize_to_limit: [1600, nil])) + " 1600w, " + rails_storage_proxy_path(landscape_asset.file.variant(resize_to_limit: [2400, nil])) + " 2400w", - sizes: "100vw" + sizes: "100vw", + 'data-controller': "image" ) ) end @@ -79,7 +90,8 @@ private srcset: rails_storage_proxy_path(asset.file.variant(resize_to_limit: [800, nil])) + " 800w, " + rails_storage_proxy_path(asset.file.variant(resize_to_limit: [1600, nil])) + " 1600w, " + rails_storage_proxy_path(asset.file.variant(resize_to_limit: [2400, nil])) + " 2400w", - sizes: "100vw" + sizes: "100vw", + 'data-controller': "image" ) end end diff --git a/app/javascript/application.js b/app/javascript/application.js index acdc181..36ef0ce 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,10 +1,13 @@ import "@hotwired/turbo-rails" import { Application } from "@hotwired/stimulus" + import LocaleController from "locale_controller" +import ImageController from "image_controller" const application = Application.start() application.register("locale", LocaleController) +application.register("image", ImageController) // Configure Stimulus development experience application.debug = false @@ -12,3 +15,444 @@ window.Stimulus = application export { application } +var animationElements +var originalHeights + + +document.addEventListener('turbo:render', (event) => { + console.info('turbo:render') + + let errorElements = document.querySelectorAll('.field_with_errors') + + if (errorElements.length > 0) { + animationElements = document.querySelectorAll('.animation-element'); + gsap.set(animationElements, { + opacity: 1 + }) + } +}); + + +// document.addEventListener("turbo:before-stream-render", animateElements) +document.addEventListener("turbo:load", animateElements) + + +function animateElements(event) { + + console.info(event.type) + + // Copy link to clipboard + document.querySelectorAll('[data-share-title]').forEach((share_btn) => { + share_btn.addEventListener('click', (e) => { + + if (navigator.share) { + navigator.share({ + title: e.currentTarget.getAttribute('data-share-title'), + text: e.currentTarget.getAttribute('data-share-text'), + url: e.currentTarget.getAttribute('data-share-url'), + }).then(() => { + console.log('Thanks for sharing!'); + }) + .catch(console.error); + } else { + navigator.clipboard.writeText(window.location.href) + alert('URL copied to clipboard!') + } + }) + }) + + originalHeights = [] + animationElements = document.querySelectorAll('.animation-element'); + + animationElements.forEach((animationElement, index) => { + const height = animationElement.offsetHeight + originalHeights.push(height) + // console.log(`Element ${index + 1} height: ${height}px`) + }) + + gsap.set(animationElements, { + height: 0, + opacity: 0, + overflow: 'hidden' + }) + + let typewriterElements = document.querySelectorAll('.typewriter-text'); + if (typewriterElements.length > 0) { + gsap.set(typewriterElements, { + opacity: 0 + }) + } + + // Start the sequential animation + animateElementsSequentially(); + +} + + + + +function animateElementsSequentially(index = 0) { + + if (index >= animationElements.length) { + console.log('All animations complete'); + return; + } + + // 'margin-top': (index == (animationElements.length-1) ? "12px" : "8px"), + + gsap.to(animationElements[index], { + height: originalHeights[index], + opacity: 1, + duration: (0.004 * originalHeights[index]), + delay: (index > 0 ? 0.4 : 0), + ease: "power2.out", + onComplete: () => { + + // Typewriter + const typewriterTexts = animationElements[index].querySelectorAll('.typewriter-text') + if (typewriterTexts.length > 0) { + const typewriter = typeWriterConsoleSequential(typewriterTexts[0].parentElement, { + baseSpeed: 20, + onAllComplete: () => { + // Start next element after typewriter finishes + animateElementsSequentially(index + 1); + } + }) + typewriter.start() + return + } + + // % Answer Distributions + const answerDistributions = animationElements[index].querySelectorAll('.answer-distribution') + if (answerDistributions.length > 0 ) { + + const containerHeight = animationElements[index].offsetHeight - 8; // - padding + + const percentages = Array.from(answerDistributions).map(bar => parseInt(bar.dataset.value)); + const maxPercentage = Math.max(...percentages); + + console.info(maxPercentage) + + answerDistributions.forEach((bar, i) => { + const percentage = parseInt(bar.dataset.value); + const relativePercentage = (percentage / maxPercentage) * 100; + const calculatedHeight = (relativePercentage / 100) * containerHeight; + + + const computedStyle = window.getComputedStyle(bar); + const minHeight = parseInt(computedStyle.minHeight) || 0; + + const targetHeight = Math.max(calculatedHeight, minHeight); + + gsap.to(bar, { + height: targetHeight, + duration: 0.4, + ease: "power2.out", + delay: index * 0.1, + onAllComplete: () => { + + if (bar.dataset.answered != undefined) { + bar.classList.add('answered') + } + + gsap.to(bar.querySelectorAll('.percentage'), { + opacity: 1, + duration: 0.4, + onAllComplete: function() { + const explanationElement = this.targets()[0].nextElementSibling + if (explanationElement) { + explanationElement.style.display = 'block' + } + } + }) + + + if (i == (answerDistributions.length - 1)) { + setTimeout(() => animateElementsSequentially(index + 1), 1000); + + } + } + }); + + }) + return + } + + + animateElementsSequentially(index + 1); + + + } + }); +} + + + + + + + +function typeWriterConsoleSequential(container, options = {}) { + const { + selector = '.typewriter-text', + baseSpeed = 60, + divDelay = 600, + showCursor = true, + onDivComplete = null, + onAllComplete = null + } = options; + + const textDivs = container.querySelectorAll(selector); + if (textDivs.length === 0) return null; + + let currentDivIndex = 0; + let isActive = false; + let animationId = null; + let timeoutIds = new Set(); // Track all timeouts for cleanup + + // Cleanup function for Turbo navigation + function cleanup() { + isActive = false; + if (animationId) { + cancelAnimationFrame(animationId); + animationId = null; + } + // Clear all timeouts + timeoutIds.forEach(id => clearTimeout(id)); + timeoutIds.clear(); + } + + // Set up Turbo event listeners for cleanup + function setupTurboListeners() { + const turboEvents = ['turbo:before-visit', 'turbo:before-cache', 'beforeunload']; + + turboEvents.forEach(eventName => { + document.addEventListener(eventName, cleanup, { once: false }); + }); + + // Store cleanup function on container for manual cleanup + container._typewriterCleanup = cleanup; + } + + // Enhanced setTimeout that tracks IDs + function safeSetTimeout(callback, delay) { + const id = setTimeout(() => { + timeoutIds.delete(id); + if (isActive) callback(); + }, delay); + timeoutIds.add(id); + return id; + } + + // Store original text and prepare divs + function initializeDivs() { + // Set container height to prevent layout shifts + const containerHeight = container.offsetHeight; + container.style.minHeight = `${containerHeight}px`; + + textDivs.forEach(div => { + if (!div.dataset.originalText) { + div.dataset.originalText = div.textContent.trim(); + } + // Use visibility instead of blanking content to maintain layout + div.style.visibility = 'hidden'; + div.innerHTML = ''; + div.style.opacity = '1'; + }); + + // Force reflow to ensure styles are applied + container.offsetHeight; + } + + function createCursorSpan() { + // No longer needed - cursor is now handled via CSS + return null; + } + + function updateDivContent(div, text, withCursor = false) { + // Use textContent for better performance + div.textContent = text; + + // Toggle cursor class instead of adding DOM element + if (withCursor && showCursor) { + div.classList.add('typewriter-cursor'); + } else { + div.classList.remove('typewriter-cursor'); + } + } + + function animateNextDiv() { + if (currentDivIndex >= textDivs.length || !isActive) { + isActive = false; + if (onAllComplete) { + // Use setTimeout to avoid potential timing issues + requestAnimationFrame(() => { + safeSetTimeout(() => onAllComplete(container), 200); + }); + } + return; + } + + const currentDiv = textDivs[currentDivIndex]; + const originalText = currentDiv.dataset.originalText; + const isLastDiv = currentDivIndex === textDivs.length - 1; + + // Make div visible and prepare for typing + currentDiv.style.visibility = 'visible'; + updateDivContent(currentDiv, '', showCursor); + currentDiv.classList.add('typing-active'); + + let charIndex = 0; + let nextCharTime = performance.now() + 200; // Initial delay + + function typeFrame(currentTime) { + if (!isActive) { + if (animationId) { + cancelAnimationFrame(animationId); + animationId = null; + } + return; + } + + if (currentTime >= nextCharTime && charIndex < originalText.length) { + charIndex++; + const currentText = originalText.substring(0, charIndex); + updateDivContent(currentDiv, currentText, true); + + // Calculate delay for next character + let delay = baseSpeed + Math.random() * 30; + const char = originalText.charAt(charIndex - 1); + + if (char === '.' || char === '!' || char === '?') { + delay += 250; + } else if (char === ',' || char === ';') { + delay += 120; + } else if (char === ' ') { + delay += 60; + } + + nextCharTime = currentTime + delay; + } + + // Check if current div is complete + if (charIndex >= originalText.length) { + currentDiv.classList.remove('typing-active'); + + // Final content update + updateDivContent(currentDiv, originalText, showCursor); + + if (onDivComplete) { + onDivComplete(currentDiv, currentDivIndex, container); + } + + currentDivIndex++; + + if (isActive && currentDivIndex < textDivs.length) { + // Move to next div after delay + safeSetTimeout(() => { + if (isActive) { + // Remove cursor from previous div (except last one) + if (!isLastDiv) { + updateDivContent(currentDiv, originalText, false); + } + animateNextDiv(); + } + }, divDelay); + } else if (isActive) { + // This was the last div + safeSetTimeout(() => { + if (onAllComplete) { + onAllComplete(container); + } + }, 200); + } + return; + } + + // Continue animation + if (isActive) { + animationId = requestAnimationFrame(typeFrame); + } + } + + // Start the animation + animationId = requestAnimationFrame(typeFrame); + } + + // Initialize Turbo listeners + setupTurboListeners(); + + // Add required CSS for cursor pseudo-element + function addCursorStyles() { + const styleId = 'typewriter-cursor-styles'; + if (!document.getElementById(styleId)) { + const style = document.createElement('style'); + style.id = styleId; + style.textContent = ` + .typewriter-cursor::after { + content: '|'; + animation: typewriter-blink 1s infinite; + color: currentColor; + } + + @keyframes typewriter-blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0; } + } + `; + document.head.appendChild(style); + } + } + + // Initialize styles + addCursorStyles(); + + return { + start: () => { + if (isActive) return; // Prevent multiple starts + + initializeDivs(); + isActive = true; + currentDivIndex = 0; + + // Use requestAnimationFrame for smoother start + requestAnimationFrame(() => { + if (isActive) { + animateNextDiv(); + } + }); + }, + + stop: () => { + cleanup(); + }, + + reset: () => { + cleanup(); + + textDivs.forEach(div => { + if (div.dataset.originalText) { + div.style.visibility = 'visible'; + updateDivContent(div, div.dataset.originalText, false); + div.classList.remove('typing-active'); + div.classList.remove('typewriter-cursor'); + } + }); + currentDivIndex = 0; + }, + + // Manual cleanup method for explicit cleanup + destroy: () => { + cleanup(); + // Remove Turbo event listeners + const turboEvents = ['turbo:before-visit', 'turbo:before-cache', 'beforeunload']; + turboEvents.forEach(eventName => { + document.removeEventListener(eventName, cleanup); + }); + // Remove cleanup reference + delete container._typewriterCleanup; + }, + + get isActive() { return isActive; } + }; +} \ No newline at end of file diff --git a/app/javascript/image_controller.js b/app/javascript/image_controller.js new file mode 100644 index 0000000..ec9d568 --- /dev/null +++ b/app/javascript/image_controller.js @@ -0,0 +1,23 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + //static targets = ["img"] + + + connect() { + + if (this.element.complete && this.element.naturalHeight !== 0) { + this.element.classList.add('loaded') + } else { + this.element.addEventListener('load', function() { + this.classList.add('loaded') + }) + } + + } + + disconnect() { + } + + +} \ No newline at end of file diff --git a/app/javascript/locale_controller.js b/app/javascript/locale_controller.js index 4a70ee6..3ce9319 100644 --- a/app/javascript/locale_controller.js +++ b/app/javascript/locale_controller.js @@ -1,12 +1,10 @@ import { Controller } from "@hotwired/stimulus" export default class extends Controller { - static targets = ["select", "current"] static values = { url: String } - connect() { - + connect() { } disconnect() { @@ -32,19 +30,27 @@ export default class extends Controller { }) .then (response => response.text()) .then(html => { + Turbo.renderStreamMessage(html) - document.documentElement.setAttribute('lang', this.selectTarget.value) - }) + document.documentElement.setAttribute('lang', this.selectTarget.value) + + requestAnimationFrame(() => { + document.querySelectorAll('.animation-element').forEach((animationElement) => { + animationElement.style.opacity = 1 + }) + + }) + + }) .catch((err) => { console.info('rejected', err) }) } - // Helper method to get CSRF token getMetaValue(name) { const element = document.head.querySelector(`meta[name="${name}"]`) return element.getAttribute("content") } -} +} \ No newline at end of file diff --git a/app/models/quiz_result.rb b/app/models/quiz_result.rb new file mode 100644 index 0000000..ee13b1f --- /dev/null +++ b/app/models/quiz_result.rb @@ -0,0 +1,15 @@ +class QuizResult < ApplicationRecord + validates :share_id, presence: true, uniqueness: true + validates :player_name, :stats, :score, presence: true + + before_validation :generate_share_id, on: :create + +private + + def generate_share_id + self.share_id = SecureRandom.urlsafe_base64(8) if share_id.blank? + # Ensure uniqueness (very unlikely collision but good practice) + generate_share_id if QuizResult.exists?(share_id: share_id) + end + +end diff --git a/app/views/languages/_intro.html.erb b/app/views/languages/_intro.html.erb index 357de2f..5f73039 100644 --- a/app/views/languages/_intro.html.erb +++ b/app/views/languages/_intro.html.erb @@ -1,7 +1,20 @@ + <% @node.attachments.limit(2).each_with_index do |attachment, i| %> - <%= tag.div attachment.body.html_safe, class: i == 0 ? "intro-content-header" : "intro-content-body" %> +
+ <% if i == 0 %> +
+ <%= decorate_divs_for_typewriting_effect(attachment.body) %> +
+ <% else %> +
+ <%= attachment.body.html_safe %> +
+ <% end %> +
<% end %> -<%= link_to t('get_started'), url_for(controller: 'players', action: 'new', locale: I18n.locale), class: 'button__base' %> - - +
+<%= link_to tag.span(t('get_started')), + url_for(controller: 'players', action: 'new', locale: I18n.locale), + class: 'button__base' %> +
\ No newline at end of file diff --git a/app/views/languages/index.html.erb b/app/views/languages/index.html.erb index b1f7e01..32d7f54 100644 --- a/app/views/languages/index.html.erb +++ b/app/views/languages/index.html.erb @@ -10,7 +10,7 @@
-
+
<%= select_tag :language, diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 555a26b..6505ecf 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -18,13 +18,15 @@ - + <%= stylesheet_link_tag "application" %> - <%= frontend_javascript_importmap_tags %w'application @hotwired/turbo-rails @hotwired/stimulus locale_controller' %> + + <%= javascript_include_tag 'gsap/gsap.min.js' %> + <%= frontend_javascript_importmap_tags %w'application @hotwired/turbo-rails @hotwired/stimulus locale_controller image_controller' %>
- <%= link_to svg('ikea-foundation-203x22'), root_url %> + <%= link_to svg('ikea-foundation-203x22'), url_for(controller: 'languages', action: 'show') %>
diff --git a/app/views/players/new.html.erb b/app/views/players/new.html.erb index 53075ae..2edce5d 100644 --- a/app/views/players/new.html.erb +++ b/app/views/players/new.html.erb @@ -7,27 +7,32 @@
- - -
- <%= t 'what_is_your_name' %> +
+
+
<%= t 'what_is_your_name' %>
+
<% if form.object.errors.any? %> +
<% form.object.errors.messages.each do |k, v| %> <%= v.join(', ') %> <% end %>
- <% end %> +
+ <% end %> - <%= form.label :name, class: "question-answer" do %> - <%= form.text_field :name, placeholder: t('what_is_your_name') %> - <% end %> +
+ <%= form.label :name, class: "question-answer" do %> + <%= form.text_field :name, placeholder: t('what_is_your_name') %> + <% end %> +
- <%= form.submit t('submit'), class: 'button__base' %> +
+ <%= form.button tag.span(t('submit')), + class: 'button__base' %> +
diff --git a/app/views/questions/answer.html.erb b/app/views/questions/answer.html.erb index 556446c..b430eff 100644 --- a/app/views/questions/answer.html.erb +++ b/app/views/questions/answer.html.erb @@ -6,28 +6,40 @@
-
-
<%= question_index %>/<%= questions_size %>
+
+
+
<%= question_index %>/<%= questions_size %>
+
-
- <% question.answer_percentages.each do |k,v| %> - <%= tag.div class: (k == player_question_answer ? 'answered' : nil) do %> - <%= tag.div v, class: 'percentage' %> - <%= t 'of_people_worldwide_think_just_lik_you' if k == player_question_answer %> +
+
+ <% question.answer_percentages.each do |k,v| %> + <%= tag.div data: { value: v, answered: (k == player_question_answer ? 1 : nil) }, class: 'answer-distribution' do %> + <%= tag.div v, class: 'percentage' %> + <%= tag.div t('of_people_worldwide_think_just_lik_you'), class: 'explanation' if k == player_question_answer %> + <% end %> <% end %> - <% end %> +
-
- <%= @question_answer.last.body.html_safe %> +
+
+ <%= @question_answer.last.body.html_safe %> +
+
<% if question == questions.last %> - <%= button_to t('see_results'), url_for(controller: 'questions', action: 'score'), class: 'button__base' %> + <%= button_to tag.span(t('see_results')), + url_for(controller: 'questions', action: 'score'), + class: 'button__base' %> <%# link_to t('see_results'), url_for(controller: 'questions', action: 'result') %> <% else %> - <%= link_to t('next_question'), url_for(controller: 'questions', action: 'show', id: question_index.next), class: 'button__base' %> + <%= link_to tag.span(t('next_question')), + url_for(controller: 'questions', action: 'show', id: question_index.next), + class: 'button__base' %> <% end %> +
\ No newline at end of file diff --git a/app/views/questions/result.html.erb b/app/views/questions/result.html.erb index a87a04e..5cbb92c 100644 --- a/app/views/questions/result.html.erb +++ b/app/views/questions/result.html.erb @@ -6,23 +6,36 @@
-
-
<%= t 'results' %>
+
+
+
<%= t 'results' %>
+
- - -
- <%= @result_attachment&.body&.sub("<name>", html_escape(current_player.name)).html_safe %> +
+
+ <%= decorate_divs_for_typewriting_effect @result_attachment&.body&.sub("<name>", html_escape(current_player.name)).html_safe %> +
-
- <%= @stats_attachment&.body&.sub("<score>", current_player.stats).html_safe %> +
+
+ <%= @stats_attachment&.body&.sub("<score>", current_player.stats).html_safe %> +
-
- <%= link_to t('read_more'), "#", class: 'button__base' %> - <%= button_tag t('share_on_story'), type: 'button', class: 'button__base' %> +
+
+ <%= link_to tag.span(t('read_more')), "#", class: 'button__base' %> + <%= button_tag tag.span(t('share_on_story')), + data: { + share_title: t('share.title'), + share_text: t('share.text'), + share_url: url_for(controller: 'questions', action: 'shared_result', id: params[:sid], only_path: false) + }, + type: 'button', + class: 'button__base' %> +
\ No newline at end of file diff --git a/app/views/questions/shared_result.html.erb b/app/views/questions/shared_result.html.erb new file mode 100644 index 0000000..3f4e24d --- /dev/null +++ b/app/views/questions/shared_result.html.erb @@ -0,0 +1,41 @@ +<%- + content_for :title, result_node.page_title.blank? ? result_node.title : result_node.page_title + content_for :meta_description, result_node.page_description +%> + + +
+ +
+
+
<%= t 'results' %>
+
+
+ +
+
+ <%= decorate_divs_for_typewriting_effect @result_attachment&.body&.sub("<name>", html_escape(@quiz_result.player_name)).html_safe %> +
+
+ +
+
+ <%= @stats_attachment&.body&.sub("<score>", @quiz_result.stats).html_safe %> +
+
+ +
+
+ <%= link_to tag.span(t('read_more')), "#", class: 'button__base' %> + <%# button_tag tag.span(t('share_on_story')), + data: { + share_title: '', + share_text: '', + share_url: url_for(controller: 'questions', action: 'shared_result', id: params[:sid]) + }, + type: 'button', + class: 'button__base' %> +
+
+ +
\ No newline at end of file diff --git a/app/views/questions/show.html.erb b/app/views/questions/show.html.erb index fd204d7..bd659a8 100644 --- a/app/views/questions/show.html.erb +++ b/app/views/questions/show.html.erb @@ -19,25 +19,32 @@
-
-
<%= question_index %>/<%= questions_size %>
+
+
+
<%= question_index %>/<%= questions_size %>
+
-
- <%= question.title %> +
+
+
<%= question.title %>
+
- <% question.attachments.with_text.each_slice(2).each_with_index do |answer_option, i| %> -
- <%= form.label :value, for: nil do %> - <%= answer_option.first.body.html_safe %> - <%= form.radio_button :value, i %> - <%- end -%> +
+ <% question.attachments.with_text.each_slice(2).each_with_index do |answer_option, i| %> +
+ <%= form.label :value, for: nil do %> + <%= answer_option.first.body.html_safe %> + <%= form.radio_button :value, i %> + <%- end -%> +
+ <% end %>
- <% end %> - <%= form.submit t('submit'), class: 'button__base' %> - +
+ <%= form.button tag.span(t('submit')), class: 'button__base' %> +
<% end %> \ No newline at end of file diff --git a/config/importmap.rb b/config/importmap.rb index b3a72c3..53f3863 100644 --- a/config/importmap.rb +++ b/config/importmap.rb @@ -15,3 +15,4 @@ pin "trix" # @2.1.15 # site_helper pin "application", preload: false pin "locale_controller", preload: false +pin "image_controller", preload: false diff --git a/config/locales/en.yml b/config/locales/en.yml index c3c1f6b..c7f33be 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -15,6 +15,10 @@ en: share_on_story: Share please_type_your_name_to_continue: Please type your name to continue + share: + title: IKEA Foundation Week quiz result + text: Quiz result description + languages: en: English diff --git a/config/routes.rb b/config/routes.rb index 84ef6c3..dece0b4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -61,9 +61,9 @@ Rails.application.routes.draw do get 'q/:id', to: 'questions#show' post 'score', to: 'questions#score' + get 'result/:id', to: 'questions#shared_result' get 'result', to: 'questions#result' - get '', to: 'languages#show' # get '*url', to: 'site#page', constraints: lambda { |req| req.path.exclude?('storage') } end diff --git a/db/migrate/20250527114853_create_quiz_results.rb b/db/migrate/20250527114853_create_quiz_results.rb new file mode 100644 index 0000000..1c603bc --- /dev/null +++ b/db/migrate/20250527114853_create_quiz_results.rb @@ -0,0 +1,15 @@ +class CreateQuizResults < ActiveRecord::Migration[8.0] + def change + create_table :quiz_results do |t| + t.string :share_id, null: false + t.string :player_name, null: false + t.string :stats, null: false + t.string :locale, null: false + t.integer :score, null: false + + t.timestamps + end + + add_index :quiz_results, :share_id, unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 2ddfeb7..b8a2c4e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2025_05_22_111116) do +ActiveRecord::Schema[8.0].define(version: 2025_05_27_114853) do # These are extensions that must be enabled in order to support this database enable_extension "pg_catalog.plpgsql" @@ -125,6 +125,17 @@ ActiveRecord::Schema[8.0].define(version: 2025_05_22_111116) do t.index ["score"], name: "index_players_on_score" end + create_table "quiz_results", force: :cascade do |t| + t.string "share_id", null: false + t.string "player_name", null: false + t.string "stats", null: false + t.string "locale", null: false + t.integer "score", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["share_id"], name: "index_quiz_results_on_share_id", unique: true + end + create_table "users", force: :cascade do |t| t.text "email" t.text "password_digest" diff --git a/public/400.html b/public/400.html index 68004c1..fc48178 100644 --- a/public/400.html +++ b/public/400.html @@ -19,9 +19,8 @@ align-items: center; } - body { - min-height: 100vh; - min-height: 100svh; + body { + min-height: 100dvh; } h2 { diff --git a/public/404.html b/public/404.html index 9fc2f1c..6446683 100644 --- a/public/404.html +++ b/public/404.html @@ -21,8 +21,7 @@ } body { - min-height: 100vh; - min-height: 100svh; + min-height: 100dvh; } h2 { diff --git a/public/406-unsupported-browser.html b/public/406-unsupported-browser.html index 103be4f..d34754c 100644 --- a/public/406-unsupported-browser.html +++ b/public/406-unsupported-browser.html @@ -17,8 +17,7 @@ } body { - min-height: 100vh; - min-height: 100svh; + min-height: 100dvh; } h2 { diff --git a/public/422.html b/public/422.html index ed97ccb..d24afd3 100644 --- a/public/422.html +++ b/public/422.html @@ -20,8 +20,7 @@ } body { - min-height: 100vh; - min-height: 100svh; + min-height: 100dvh; } h2 { diff --git a/public/500.html b/public/500.html index ec9a2e5..8aa5b87 100644 --- a/public/500.html +++ b/public/500.html @@ -20,8 +20,7 @@ } body { - min-height: 100vh; - min-height: 100svh; + min-height: 100dvh; } h2 { diff --git a/test/fixtures/quiz_results.yml b/test/fixtures/quiz_results.yml new file mode 100644 index 0000000..d7a3329 --- /dev/null +++ b/test/fixtures/quiz_results.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the "{}" from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/quiz_result_test.rb b/test/models/quiz_result_test.rb new file mode 100644 index 0000000..1d0251f --- /dev/null +++ b/test/models/quiz_result_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class QuizResultTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/vendor/javascript/gsap/.DS_Store b/vendor/javascript/gsap/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 typeof(window) !== \"undefined\",\n\t_getGSAP = () => gsap || (_windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap),\n\t_checkRegister = () => {\n\t\tif (!_coreInitted) {\n\t\t\t_initCore();\n\t\t\tif (!CSSPlugin) {\n\t\t\t\tconsole.warn(\"Please gsap.registerPlugin(CSSPlugin, CSSRulePlugin)\");\n\t\t\t}\n\t\t}\n\t\treturn _coreInitted;\n\t},\n\t_initCore = core => {\n\t\tgsap = core || _getGSAP();\n\t\tif (_windowExists()) {\n\t\t\t_win = window;\n\t\t\t_doc = document;\n\t\t}\n\t\tif (gsap) {\n\t\t\tCSSPlugin = gsap.plugins.css;\n\t\t\tif (CSSPlugin) {\n\t\t\t\t_coreInitted = 1;\n\t\t\t}\n\t\t}\n\t};\n\n\nexport const CSSRulePlugin = {\n\tversion: \"3.13.0\",\n\tname: \"cssRule\",\n\tinit(target, value, tween, index, targets) {\n\t\tif (!_checkRegister() || typeof(target.cssText) === \"undefined\") {\n\t\t\treturn false;\n\t\t}\n\t\tlet div = target._gsProxy = target._gsProxy || _doc.createElement(\"div\");\n\t\tthis.ss = target;\n\t\tthis.style = div.style;\n\t\tdiv.style.cssText = target.cssText;\n\t\tCSSPlugin.prototype.init.call(this, div, value, tween, index, targets); //we just offload all the work to the regular CSSPlugin and then copy the cssText back over to the rule in the render() method. This allows us to have all of the updates to CSSPlugin automatically flow through to CSSRulePlugin instead of having to maintain both\n\t},\n\trender(ratio, data) {\n\t\tlet pt = data._pt,\n\t\t\tstyle = data.style,\n\t\t\tss = data.ss,\n\t\t\ti;\n\t\twhile (pt) {\n\t\t\tpt.r(ratio, pt.d);\n\t\t\tpt = pt._next;\n\t\t}\n\t\ti = style.length;\n\t\twhile (--i > -1) {\n\t\t\tss[style[i]] = style[style[i]];\n\t\t}\n\t},\n\tgetRule(selector) {\n\t\t_checkRegister();\n\t\tlet ruleProp = _doc.all ? \"rules\" : \"cssRules\",\n\t\t\tstyleSheets = _doc.styleSheets,\n\t\t\ti = styleSheets.length,\n\t\t\tpseudo = (selector.charAt(0) === \":\"),\n\t\t\tj, curSS, cs, a;\n\t\tselector = (pseudo ? \"\" : \",\") + selector.split(\"::\").join(\":\").toLowerCase() + \",\"; //note: old versions of IE report tag name selectors as upper case, so we just change everything to lowercase.\n\t\tif (pseudo) {\n\t\t\ta = [];\n\t\t}\n\t\twhile (i--) {\n\t\t\t//Firefox may throw insecure operation errors when css is loaded from other domains, so try/catch.\n\t\t\ttry {\n\t\t\t\tcurSS = styleSheets[i][ruleProp];\n\t\t\t\tif (!curSS) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tj = curSS.length;\n\t\t\t} catch (e) {\n\t\t\t\tconsole.warn(e);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\twhile (--j > -1) {\n\t\t\t\tcs = curSS[j];\n\t\t\t\tif (cs.selectorText && (\",\" + cs.selectorText.split(\"::\").join(\":\").toLowerCase() + \",\").indexOf(selector) !== -1) { //note: IE adds an extra \":\" to pseudo selectors, so .myClass:after becomes .myClass::after, so we need to strip the extra one out.\n\t\t\t\t\tif (pseudo) {\n\t\t\t\t\t\ta.push(cs.style);\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn cs.style;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn a;\n\t},\n\tregister: _initCore\n};\n\n_getGSAP() && gsap.registerPlugin(CSSRulePlugin);\n\nexport { CSSRulePlugin as default };"],"names":["_windowExists","window","_getGSAP","gsap","registerPlugin","_checkRegister","_coreInitted","_initCore","CSSPlugin","console","warn","_doc","core","document","plugins","css","CSSRulePlugin","version","name","init","target","value","tween","index","targets","cssText","div","_gsProxy","createElement","ss","style","prototype","call","this","render","ratio","data","i","pt","_pt","r","d","_next","length","getRule","selector","j","curSS","cs","a","ruleProp","all","styleSheets","pseudo","charAt","split","join","toLowerCase","e","selectorText","indexOf","push","register"],"mappings":";;;;;;;;;6MAWiB,SAAhBA,UAAyC,oBAAZC,OAClB,SAAXC,WAAiBC,GAASH,MAAoBG,EAAOF,OAAOE,OAASA,EAAKC,gBAAkBD,EAC3E,SAAjBE,WACMC,IACJC,IACKC,GACJC,QAAQC,KAAK,yDAGRJ,MAVLH,EAAMG,EAAoBK,EAAMH,EAYnCD,EAAY,SAAZA,UAAYK,GACXT,EAAOS,GAAQV,IACXF,MAEHW,EAAOE,UAEJV,IACHK,EAAYL,EAAKW,QAAQC,OAExBT,EAAe,IAMNU,EAAgB,CAC5BC,QAAS,SACTC,KAAM,UACNC,mBAAKC,EAAQC,EAAOC,EAAOC,EAAOC,OAC5BnB,UAA+C,IAApBe,EAAOK,eAC/B,MAEJC,EAAMN,EAAOO,SAAWP,EAAOO,UAAYhB,EAAKiB,cAAc,YAC7DC,GAAKT,OACLU,MAAQJ,EAAII,MACjBJ,EAAII,MAAML,QAAUL,EAAOK,QAC3BjB,EAAUuB,UAAUZ,KAAKa,KAAKC,KAAMP,EAAKL,EAAOC,EAAOC,EAAOC,IAE/DU,uBAAOC,EAAOC,WAIZC,EAHGC,EAAKF,EAAKG,IACbT,EAAQM,EAAKN,MACbD,EAAKO,EAAKP,GAEJS,GACNA,EAAGE,EAAEL,EAAOG,EAAGG,GACfH,EAAKA,EAAGI,UAETL,EAAIP,EAAMa,QACI,IAALN,GACRR,EAAGC,EAAMO,IAAMP,EAAMA,EAAMO,KAG7BO,yBAAQC,GACPxC,QAKCyC,EAAGC,EAAOC,EAAIC,EAJXC,EAAWvC,EAAKwC,IAAM,QAAU,WACnCC,EAAczC,EAAKyC,YACnBf,EAAIe,EAAYT,OAChBU,EAAiC,MAAvBR,EAASS,OAAO,OAE3BT,GAAYQ,EAAS,GAAK,KAAOR,EAASU,MAAM,MAAMC,KAAK,KAAKC,cAAgB,IAC5EJ,IACHJ,EAAI,IAEEZ,KAAK,UAGVU,EAAQK,EAAYf,GAAGa,aAIvBJ,EAAIC,EAAMJ,OACT,MAAOe,GACRjD,QAAQC,KAAKgD,kBAGA,IAALZ,OACRE,EAAKD,EAAMD,IACJa,eAAyG,KAAxF,IAAMX,EAAGW,aAAaJ,MAAM,MAAMC,KAAK,KAAKC,cAAgB,KAAKG,QAAQf,GAAkB,KAC9GQ,SAGIL,EAAGlB,MAFVmB,EAAEY,KAAKb,EAAGlB,eAOPmB,GAERa,SAAUvD,GAGXL,KAAcC,EAAKC,eAAeY"} \ No newline at end of file diff --git a/vendor/javascript/gsap/CustomBounce.min.js b/vendor/javascript/gsap/CustomBounce.min.js new file mode 100644 index 0000000..f7ba41c --- /dev/null +++ b/vendor/javascript/gsap/CustomBounce.min.js @@ -0,0 +1,11 @@ +/*! + * CustomBounce 3.13.0 + * https://gsap.com + * + * @license Copyright 2025, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,n){"object"==typeof exports&&"undefined"!=typeof module?n(exports):"function"==typeof define&&define.amd?define(["exports"],n):n((e=e||self).window=e.window||{})}(this,function(e){"use strict";function g(){return n||"undefined"!=typeof window&&(n=window.gsap)&&n.registerPlugin&&n}function h(e){n=g(),(j=n&&n.parseEase("_CE"))?(b=1,n.parseEase("bounce").config=function(e){return"object"==typeof e?t("",e):t("bounce("+e+")",{strength:+e})}):e&&console.warn("Please gsap.registerPlugin(CustomEase, CustomBounce)")}function i(e){var n,t=e.length,o=1/e[t-2];for(n=2;n gsap || (typeof(window) !== \"undefined\" && (gsap = window.gsap) && gsap.registerPlugin && gsap),\n\t_initCore = required => {\n\t\tgsap = _getGSAP();\n\t\tcreateCustomEase = gsap && gsap.parseEase(\"_CE\");\n\t\tif (createCustomEase) {\n\t\t\t_coreInitted = 1;\n\t\t\tgsap.parseEase(\"bounce\").config = vars => typeof(vars) === \"object\" ? _create(\"\", vars) : _create(\"bounce(\" + vars + \")\", {strength:+vars});\n\t\t} else {\n\t\t\trequired && console.warn(\"Please gsap.registerPlugin(CustomEase, CustomBounce)\");\n\t\t}\n\t},\n\t_normalizeX = a => { //scales all the x values in an array [x, y, x, y...] AND rounds them to the closest hundredth (decimal)\n\t\tlet l = a.length,\n\t\t\ts = 1 / a[l - 2],\n\t\t\trnd = 1000,\n\t\t\ti;\n\t\tfor (i = 2; i < l; i += 2) {\n\t\t\ta[i] = ~~(a[i] * s * rnd) / rnd;\n\t\t}\n\t\ta[l - 2] = 1; //in case there are any rounding errors. x should always end at 1.\n\t},\n\t_bonusValidated = 1, //CustomBounce\n\t_create = (id, vars) => {\n\t\tif (!_coreInitted) {\n\t\t\t_initCore(1);\n\t\t}\n\t\tvars = vars || {};\n\t\tif (_bonusValidated) {\n\t\t\tlet max = 0.999,\n\t\t\t\tdecay = Math.min(max, vars.strength || 0.7), // Math.min(0.999, 1 - 0.3 / (vars.strength || 1)),\n\t\t\t\tdecayX = decay,\n\t\t\t\tgap = (vars.squash || 0) / 100,\n\t\t\t\toriginalGap = gap,\n\t\t\t\tslope = 1 / 0.03,\n\t\t\t\tw = 0.2,\n\t\t\t\th = 1,\n\t\t\t\tprevX = 0.1,\n\t\t\t\tpath = [0, 0, 0.07, 0, 0.1, 1, 0.1, 1],\n\t\t\t\tsquashPath = [0, 0, 0, 0, 0.1, 0, 0.1, 0],\n\t\t\t\tcp1, cp2, x, y, i, nextX, squishMagnitude;\n\t\t\tfor (i = 0; i < 200; i++) {\n\t\t\t\tw *= decayX * ((decayX + 1) / 2);\n\t\t\t\th *= decay * decay;\n\t\t\t\tnextX = prevX + w;\n\t\t\t\tx = prevX + w * 0.49;\n\t\t\t\ty = 1 - h;\n\t\t\t\tcp1 = prevX + h / slope;\n\t\t\t\tcp2 = x + (x - cp1) * 0.8;\n\n\t\t\t\tif (gap) {\n\t\t\t\t\tprevX += gap;\n\t\t\t\t\tcp1 += gap;\n\t\t\t\t\tx += gap;\n\t\t\t\t\tcp2 += gap;\n\t\t\t\t\tnextX += gap;\n\t\t\t\t\tsquishMagnitude = gap / originalGap;\n\t\t\t\t\tsquashPath.push(\n\t\t\t\t\t\tprevX - gap, 0,\n\t\t\t\t\t\tprevX - gap, squishMagnitude,\n\t\t\t\t\t\tprevX - gap / 2, squishMagnitude, //center peak anchor\n\t\t\t\t\t\tprevX, squishMagnitude,\n\t\t\t\t\t\tprevX, 0,\n\t\t\t\t\t\tprevX, 0, //base anchor\n\t\t\t\t\t\tprevX, squishMagnitude * -0.6,\n\t\t\t\t\t\tprevX + (nextX - prevX) / 6, 0,\n\t\t\t\t\t\tnextX, 0\n\t\t\t\t\t);\n\t\t\t\t\tpath.push(prevX - gap, 1,\n\t\t\t\t\t\tprevX, 1,\n\t\t\t\t\t\tprevX, 1);\n\t\t\t\t\tgap *= decay * decay;\n\t\t\t\t}\n\n\t\t\t\tpath.push(prevX, 1,\n\t\t\t\t\tcp1, y,\n\t\t\t\t\tx, y,\n\t\t\t\t\tcp2, y,\n\t\t\t\t\tnextX, 1,\n\t\t\t\t\tnextX, 1);\n\n\t\t\t\tdecay *= 0.95;\n\t\t\t\tslope = h / (nextX - cp2);\n\t\t\t\tprevX = nextX;\n\t\t\t\tif (y > max) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (vars.endAtStart && vars.endAtStart !== \"false\") {\n\t\t\t\tx = -0.1;\n\t\t\t\tpath.unshift(x, 1, x, 1, -0.07, 0);\n\t\t\t\tif (originalGap) {\n\t\t\t\t\tgap = originalGap * 2.5; //make the initial anticipation squash longer (more realistic)\n\t\t\t\t\tx -= gap;\n\t\t\t\t\tpath.unshift(x, 1, x, 1, x, 1);\n\t\t\t\t\tsquashPath.splice(0, 6);\n\t\t\t\t\tsquashPath.unshift(x, 0, x, 0, x, 1, x + gap / 2, 1, x + gap, 1, x + gap, 0, x + gap, 0, x + gap, -0.6, x + gap + 0.033, 0);\n\t\t\t\t\tfor (i = 0; i < squashPath.length; i+=2) {\n\t\t\t\t\t\tsquashPath[i] -= x;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < path.length; i+=2) {\n\t\t\t\t\tpath[i] -= x;\n\t\t\t\t\tpath[i+1] = 1 - path[i+1];\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (gap) {\n\t\t\t\t_normalizeX(squashPath);\n\t\t\t\tsquashPath[2] = \"C\" + squashPath[2];\n\t\t\t\tcreateCustomEase(vars.squashID || (id + \"-squash\"), \"M\" + squashPath.join(\",\"));\n\t\t\t}\n\t\t\t_normalizeX(path);\n\t\t\tpath[2] = \"C\" + path[2];\n\t\t\treturn createCustomEase(id, \"M\" + path.join(\",\"));\n\t\t}\n\t};\n\nexport class CustomBounce {\n\n\tconstructor(id, vars) {\n\t\tthis.ease = _create(id, vars);\n\t}\n\n\tstatic create(id, vars) {\n\t\treturn _create(id, vars);\n\t}\n\n\tstatic register(core) {\n\t\tgsap = core;\n\t\t_initCore();\n\t}\n\n}\n\n_getGSAP() && gsap.registerPlugin(CustomBounce);\n\nCustomBounce.version = \"3.13.0\";\n\nexport { CustomBounce as default };"],"names":["_getGSAP","gsap","window","registerPlugin","_initCore","required","createCustomEase","parseEase","_coreInitted","config","vars","_create","strength","console","warn","_normalizeX","a","i","l","length","s","id","cp1","cp2","x","y","nextX","squishMagnitude","decay","Math","min","decayX","gap","squash","originalGap","slope","w","h","prevX","path","squashPath","push","endAtStart","unshift","splice","squashID","join","CustomBounce","create","register","core","ease","version"],"mappings":"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"} \ No newline at end of file diff --git a/vendor/javascript/gsap/CustomEase.min.js b/vendor/javascript/gsap/CustomEase.min.js new file mode 100644 index 0000000..6712347 --- /dev/null +++ b/vendor/javascript/gsap/CustomEase.min.js @@ -0,0 +1,11 @@ +/*! + * CustomEase 3.13.0 + * https://gsap.com + * + * @license Copyright 2025, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function m(e){return Math.round(1e5*e)/1e5||0}var b=/[achlmqstvz]|(-?\d*\.?\d*(?:e[\-+]?\d+)?)[0-9]/gi,w=/[\+\-]?\d*\.?\d+e[\+\-]?\d+/gi,Y=Math.PI/180,k=Math.sin,B=Math.cos,F=Math.abs,J=Math.sqrt;function arcToSegment(e,t,n,s,a,r,i,o,h){if(e!==o||t!==h){n=F(n),s=F(s);var u=a%360*Y,c=B(u),f=k(u),l=Math.PI,g=2*l,x=(e-o)/2,d=(t-h)/2,m=c*x+f*d,p=-f*x+c*d,y=m*m,M=p*p,v=y/(n*n)+M/(s*s);1u.x||u.y!==h.y&&u.x===h.x||h===u)&&h.x<=1?(u.cx=h.x-u.x,u.cy=h.y-u.y,u.n=h,u.nx=h.x,p&&1c||r===u-1)&&(n.push(f,l),a=(h-l)/(o-f)),f=o,l=h;n="M"+n.join(",")}return y&&y.setAttribute("d",n),n},CustomEase);function CustomEase(e,t,n){a||q(),this.id=e,this.setData(t,n)}n.version="3.13.0",n.headless=!0,p()&&M.registerPlugin(n),e.CustomEase=n,e.default=n;if (typeof(window)==="undefined"||window!==e){Object.defineProperty(e,"__esModule",{value:!0})} else {delete e.default}}); + diff --git a/vendor/javascript/gsap/CustomEase.min.js.map b/vendor/javascript/gsap/CustomEase.min.js.map new file mode 100644 index 0000000..a247324 --- /dev/null +++ b/vendor/javascript/gsap/CustomEase.min.js.map @@ -0,0 +1 @@ +{"version":3,"file":"CustomEase.min.js","sources":["../src/utils/paths.js","../src/CustomEase.js"],"sourcesContent":["/*!\n * paths 3.13.0\n * https://gsap.com\n *\n * Copyright 2008-2025, GreenSock. All rights reserved.\n * Subject to the terms at https://gsap.com/standard-license\n * @author: Jack Doyle, jack@greensock.com\n*/\n/* eslint-disable */\n\nlet _svgPathExp = /[achlmqstvz]|(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[0-9]/ig,\n\t_numbersExp = /(?:(-)?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[0-9]/ig,\n\t_scientific = /[\\+\\-]?\\d*\\.?\\d+e[\\+\\-]?\\d+/ig,\n\t_selectorExp = /(^[#\\.][a-z]|[a-y][a-z])/i,\n\t_DEG2RAD = Math.PI / 180,\n\t_RAD2DEG = 180 / Math.PI,\n\t_sin = Math.sin,\n\t_cos = Math.cos,\n\t_abs = Math.abs,\n\t_sqrt = Math.sqrt,\n\t_atan2 = Math.atan2,\n\t_largeNum = 1e8,\n\t_isString = value => typeof(value) === \"string\",\n\t_isNumber = value => typeof(value) === \"number\",\n\t_isUndefined = value => typeof(value) === \"undefined\",\n\t_temp = {},\n\t_temp2 = {},\n\t_roundingNum = 1e5,\n\t_wrapProgress = progress => (Math.round((progress + _largeNum) % 1 * _roundingNum) / _roundingNum) || ((progress < 0) ? 0 : 1), //if progress lands on 1, the % will make it 0 which is why we || 1, but not if it's negative because it makes more sense for motion to end at 0 in that case.\n\t_round = value => (Math.round(value * _roundingNum) / _roundingNum) || 0,\n\t_roundPrecise = value => (Math.round(value * 1e10) / 1e10) || 0,\n\t_splitSegment = (rawPath, segIndex, i, t) => {\n\t\tlet segment = rawPath[segIndex],\n\t\t\tshift = t === 1 ? 6 : subdivideSegment(segment, i, t);\n\t\tif ((shift || !t) && shift + i + 2 < segment.length) {\n\t\t\trawPath.splice(segIndex, 0, segment.slice(0, i + shift + 2));\n\t\t\tsegment.splice(0, i + shift);\n\t\t\treturn 1;\n\t\t}\n\t},\n\t_getSampleIndex = (samples, length, progress) => {\n\t\t// slightly slower way than doing this (when there's no lookup): segment.lookup[progress < 1 ? ~~(length / segment.minLength) : segment.lookup.length - 1] || 0;\n\t\tlet l = samples.length,\n\t\t\ti = ~~(progress * l);\n\t\tif (samples[i] > length) {\n\t\t\twhile (--i && samples[i] > length) {}\n\t\t\ti < 0 && (i = 0);\n\t\t} else {\n\t\t\twhile (samples[++i] < length && i < l) {}\n\t\t}\n\t\treturn i < l ? i : l - 1;\n\t},\n\t_reverseRawPath = (rawPath, skipOuter) => {\n\t\tlet i = rawPath.length;\n\t\tskipOuter || rawPath.reverse();\n\t\twhile (i--) {\n\t\t\trawPath[i].reversed || reverseSegment(rawPath[i]);\n\t\t}\n\t},\n\t_copyMetaData = (source, copy) => {\n\t\tcopy.totalLength = source.totalLength;\n\t\tif (source.samples) { //segment\n\t\t\tcopy.samples = source.samples.slice(0);\n\t\t\tcopy.lookup = source.lookup.slice(0);\n\t\t\tcopy.minLength = source.minLength;\n\t\t\tcopy.resolution = source.resolution;\n\t\t} else if (source.totalPoints) { //rawPath\n\t\t\tcopy.totalPoints = source.totalPoints;\n\t\t}\n\t\treturn copy;\n\t},\n\t//pushes a new segment into a rawPath, but if its starting values match the ending values of the last segment, it'll merge it into that same segment (to reduce the number of segments)\n\t_appendOrMerge = (rawPath, segment) => {\n\t\tlet index = rawPath.length,\n\t\t\tprevSeg = rawPath[index - 1] || [],\n\t\t\tl = prevSeg.length;\n\t\tif (index && segment[0] === prevSeg[l-2] && segment[1] === prevSeg[l-1]) {\n\t\t\tsegment = prevSeg.concat(segment.slice(2));\n\t\t\tindex--;\n\t\t}\n\t\trawPath[index] = segment;\n\t},\n\t_bestDistance;\n\n/* TERMINOLOGY\n - RawPath - an array of arrays, one for each Segment. A single RawPath could have multiple \"M\" commands, defining Segments (paths aren't always connected).\n - Segment - an array containing a sequence of Cubic Bezier coordinates in alternating x, y, x, y format. Starting anchor, then control point 1, control point 2, and ending anchor, then the next control point 1, control point 2, anchor, etc. Uses less memory than an array with a bunch of {x, y} points.\n - Bezier - a single cubic Bezier with a starting anchor, two control points, and an ending anchor.\n - the variable \"t\" is typically the position along an individual Bezier path (time) and it's NOT linear, meaning it could accelerate/decelerate based on the control points whereas the \"p\" or \"progress\" value is linearly mapped to the whole path, so it shouldn't really accelerate/decelerate based on control points. So a progress of 0.2 would be almost exactly 20% along the path. \"t\" is ONLY in an individual Bezier piece.\n */\n\n//accepts basic selector text, a path instance, a RawPath instance, or a Segment and returns a RawPath (makes it easy to homogenize things). If an element or selector text is passed in, it'll also cache the value so that if it's queried again, it'll just take the path data from there instead of parsing it all over again (as long as the path data itself hasn't changed - it'll check).\nexport function getRawPath(value) {\n\tvalue = (_isString(value) && _selectorExp.test(value)) ? document.querySelector(value) || value : value;\n\tlet e = value.getAttribute ? value : 0,\n\t\trawPath;\n\tif (e && (value = value.getAttribute(\"d\"))) {\n\t\t//implements caching\n\t\tif (!e._gsPath) {\n\t\t\te._gsPath = {};\n\t\t}\n\t\trawPath = e._gsPath[value];\n\t\treturn (rawPath && !rawPath._dirty) ? rawPath : (e._gsPath[value] = stringToRawPath(value));\n\t}\n\treturn !value ? console.warn(\"Expecting a element or an SVG path data string\") : _isString(value) ? stringToRawPath(value) : (_isNumber(value[0])) ? [value] : value;\n}\n\n//copies a RawPath WITHOUT the length meta data (for speed)\nexport function copyRawPath(rawPath) {\n\tlet a = [],\n\t\ti = 0;\n\tfor (; i < rawPath.length; i++) {\n\t\ta[i] = _copyMetaData(rawPath[i], rawPath[i].slice(0));\n\t}\n\treturn _copyMetaData(rawPath, a);\n}\n\nexport function reverseSegment(segment) {\n\tlet i = 0,\n\t\ty;\n\tsegment.reverse(); //this will invert the order y, x, y, x so we must flip it back.\n\tfor (; i < segment.length; i += 2) {\n\t\ty = segment[i];\n\t\tsegment[i] = segment[i+1];\n\t\tsegment[i+1] = y;\n\t}\n\tsegment.reversed = !segment.reversed;\n}\n\n\n\nlet _createPath = (e, ignore) => {\n\t\tlet path = document.createElementNS(\"http://www.w3.org/2000/svg\", \"path\"),\n\t\t\tattr = [].slice.call(e.attributes),\n\t\t\ti = attr.length,\n\t\t\tname;\n\t\tignore = \",\" + ignore + \",\";\n\t\twhile (--i > -1) {\n\t\t\tname = attr[i].nodeName.toLowerCase(); //in Microsoft Edge, if you don't set the attribute with a lowercase name, it doesn't render correctly! Super weird.\n\t\t\tif (ignore.indexOf(\",\" + name + \",\") < 0) {\n\t\t\t\tpath.setAttributeNS(null, name, attr[i].nodeValue);\n\t\t\t}\n\t\t}\n\t\treturn path;\n\t},\n\t_typeAttrs = {\n\t\trect:\"rx,ry,x,y,width,height\",\n\t\tcircle:\"r,cx,cy\",\n\t\tellipse:\"rx,ry,cx,cy\",\n\t\tline:\"x1,x2,y1,y2\"\n\t},\n\t_attrToObj = (e, attrs) => {\n\t\tlet props = attrs ? attrs.split(\",\") : [],\n\t\t\tobj = {},\n\t\t\ti = props.length;\n\t\twhile (--i > -1) {\n\t\t\tobj[props[i]] = +e.getAttribute(props[i]) || 0;\n\t\t}\n\t\treturn obj;\n\t};\n\n//converts an SVG shape like , , , , , etc. to a , swapping it in and copying the attributes to match.\nexport function convertToPath(element, swap) {\n\tlet type = element.tagName.toLowerCase(),\n\t\tcirc = 0.552284749831,\n\t\tdata, x, y, r, ry, path, rcirc, rycirc, points, w, h, x2, x3, x4, x5, x6, y2, y3, y4, y5, y6, attr;\n\tif (type === \"path\" || !element.getBBox) {\n\t\treturn element;\n\t}\n\tpath = _createPath(element, \"x,y,width,height,cx,cy,rx,ry,r,x1,x2,y1,y2,points\");\n\tattr = _attrToObj(element, _typeAttrs[type]);\n\tif (type === \"rect\") {\n\t\tr = attr.rx;\n\t\try = attr.ry || r;\n\t\tx = attr.x;\n\t\ty = attr.y;\n\t\tw = attr.width - r * 2;\n\t\th = attr.height - ry * 2;\n\t\tif (r || ry) { //if there are rounded corners, render cubic beziers\n\t\t\tx2 = x + r * (1 - circ);\n\t\t\tx3 = x + r;\n\t\t\tx4 = x3 + w;\n\t\t\tx5 = x4 + r * circ;\n\t\t\tx6 = x4 + r;\n\t\t\ty2 = y + ry * (1 - circ);\n\t\t\ty3 = y + ry;\n\t\t\ty4 = y3 + h;\n\t\t\ty5 = y4 + ry * circ;\n\t\t\ty6 = y4 + ry;\n\t\t\tdata = \"M\" + x6 + \",\" + y3 + \" V\" + y4 + \" C\" + [x6, y5, x5, y6, x4, y6, x4 - (x4 - x3) / 3, y6, x3 + (x4 - x3) / 3, y6, x3, y6, x2, y6, x, y5, x, y4, x, y4 - (y4 - y3) / 3, x, y3 + (y4 - y3) / 3, x, y3, x, y2, x2, y, x3, y, x3 + (x4 - x3) / 3, y, x4 - (x4 - x3) / 3, y, x4, y, x5, y, x6, y2, x6, y3].join(\",\") + \"z\";\n\t\t} else {\n\t\t\tdata = \"M\" + (x + w) + \",\" + y + \" v\" + h + \" h\" + (-w) + \" v\" + (-h) + \" h\" + w + \"z\";\n\t\t}\n\n\t} else if (type === \"circle\" || type === \"ellipse\") {\n\t\tif (type === \"circle\") {\n\t\t\tr = ry = attr.r;\n\t\t\trycirc = r * circ;\n\t\t} else {\n\t\t\tr = attr.rx;\n\t\t\try = attr.ry;\n\t\t\trycirc = ry * circ;\n\t\t}\n\t\tx = attr.cx;\n\t\ty = attr.cy;\n\t\trcirc = r * circ;\n\t\tdata = \"M\" + (x+r) + \",\" + y + \" C\" + [x+r, y + rycirc, x + rcirc, y + ry, x, y + ry, x - rcirc, y + ry, x - r, y + rycirc, x - r, y, x - r, y - rycirc, x - rcirc, y - ry, x, y - ry, x + rcirc, y - ry, x + r, y - rycirc, x + r, y].join(\",\") + \"z\";\n\t} else if (type === \"line\") {\n\t\tdata = \"M\" + attr.x1 + \",\" + attr.y1 + \" L\" + attr.x2 + \",\" + attr.y2; //previously, we just converted to \"Mx,y Lx,y\" but Safari has bugs that cause that not to render properly when using a stroke-dasharray that's not fully visible! Using a cubic bezier fixes that issue.\n\t} else if (type === \"polyline\" || type === \"polygon\") {\n\t\tpoints = (element.getAttribute(\"points\") + \"\").match(_numbersExp) || [];\n\t\tx = points.shift();\n\t\ty = points.shift();\n\t\tdata = \"M\" + x + \",\" + y + \" L\" + points.join(\",\");\n\t\tif (type === \"polygon\") {\n\t\t\tdata += \",\" + x + \",\" + y + \"z\";\n\t\t}\n\t}\n\tpath.setAttribute(\"d\", rawPathToString(path._gsRawPath = stringToRawPath(data)));\n\tif (swap && element.parentNode) {\n\t\telement.parentNode.insertBefore(path, element);\n\t\telement.parentNode.removeChild(element);\n\t}\n\treturn path;\n}\n\n\n\n//returns the rotation (in degrees) at a particular progress on a rawPath (the slope of the tangent)\nexport function getRotationAtProgress(rawPath, progress) {\n\tlet d = getProgressData(rawPath, progress >= 1 ? 1 - 1e-9 : progress ? progress : 1e-9);\n\treturn getRotationAtBezierT(d.segment, d.i, d.t);\n}\n\nfunction getRotationAtBezierT(segment, i, t) {\n\tlet a = segment[i],\n\t\tb = segment[i+2],\n\t\tc = segment[i+4],\n\t\tx;\n\ta += (b - a) * t;\n\tb += (c - b) * t;\n\ta += (b - a) * t;\n\tx = b + ((c + (segment[i+6] - c) * t) - b) * t - a;\n\ta = segment[i+1];\n\tb = segment[i+3];\n\tc = segment[i+5];\n\ta += (b - a) * t;\n\tb += (c - b) * t;\n\ta += (b - a) * t;\n\treturn _round(_atan2(b + ((c + (segment[i+7] - c) * t) - b) * t - a, x) * _RAD2DEG);\n}\n\nexport function sliceRawPath(rawPath, start, end) {\n\tend = _isUndefined(end) ? 1 : _roundPrecise(end) || 0; // we must round to avoid issues like 4.15 / 8 = 0.8300000000000001 instead of 0.83 or 2.8 / 5 = 0.5599999999999999 instead of 0.56 and if someone is doing a loop like start: 2.8 / 0.5, end: 2.8 / 0.5 + 1.\n\tstart = _roundPrecise(start) || 0;\n\tlet loops = Math.max(0, ~~(_abs(end - start) - 1e-8)),\n\t\tpath = copyRawPath(rawPath);\n\tif (start > end) {\n\t\tstart = 1 - start;\n\t\tend = 1 - end;\n\t\t_reverseRawPath(path);\n\t\tpath.totalLength = 0;\n\t}\n\tif (start < 0 || end < 0) {\n\t\tlet offset = Math.abs(~~Math.min(start, end)) + 1;\n\t\tstart += offset;\n\t\tend += offset;\n\t}\n\tpath.totalLength || cacheRawPathMeasurements(path);\n\tlet wrap = (end > 1),\n\t\ts = getProgressData(path, start, _temp, true),\n\t\te = getProgressData(path, end, _temp2),\n\t\teSeg = e.segment,\n\t\tsSeg = s.segment,\n\t\teSegIndex = e.segIndex,\n\t\tsSegIndex = s.segIndex,\n\t\tei = e.i,\n\t\tsi = s.i,\n\t\tsameSegment = (sSegIndex === eSegIndex),\n\t\tsameBezier = (ei === si && sameSegment),\n\t\twrapsBehind, sShift, eShift, i, copy, totalSegments, l, j;\n\tif (wrap || loops) {\n\t\twrapsBehind = eSegIndex < sSegIndex || (sameSegment && ei < si) || (sameBezier && e.t < s.t);\n\t\tif (_splitSegment(path, sSegIndex, si, s.t)) {\n\t\t\tsSegIndex++;\n\t\t\tif (!wrapsBehind) {\n\t\t\t\teSegIndex++;\n\t\t\t\tif (sameBezier) {\n\t\t\t\t\te.t = (e.t - s.t) / (1 - s.t);\n\t\t\t\t\tei = 0;\n\t\t\t\t} else if (sameSegment) {\n\t\t\t\t\tei -= si;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (Math.abs(1 - (end - start)) < 1e-5) {\n\t\t\teSegIndex = sSegIndex - 1;\n\t\t} else if (!e.t && eSegIndex) {\n\t\t\teSegIndex--;\n\t\t} else if (_splitSegment(path, eSegIndex, ei, e.t) && wrapsBehind) {\n\t\t\tsSegIndex++;\n\t\t}\n\t\tif (s.t === 1) {\n\t\t\tsSegIndex = (sSegIndex + 1) % path.length;\n\t\t}\n\t\tcopy = [];\n\t\ttotalSegments = path.length;\n\t\tl = 1 + totalSegments * loops;\n\t\tj = sSegIndex;\n\t\tl += ((totalSegments - sSegIndex) + eSegIndex) % totalSegments;\n\t\tfor (i = 0; i < l; i++) {\n\t\t\t_appendOrMerge(copy, path[j++ % totalSegments]);\n\t\t}\n\t\tpath = copy;\n\t} else {\n\t\teShift = e.t === 1 ? 6 : subdivideSegment(eSeg, ei, e.t);\n\t\tif (start !== end) {\n\t\t\tsShift = subdivideSegment(sSeg, si, sameBezier ? s.t / e.t : s.t);\n\t\t\tsameSegment && (eShift += sShift);\n\t\t\teSeg.splice(ei + eShift + 2);\n\t\t\t(sShift || si) && sSeg.splice(0, si + sShift);\n\t\t\ti = path.length;\n\t\t\twhile (i--) {\n\t\t\t\t//chop off any extra segments\n\t\t\t\t(i < sSegIndex || i > eSegIndex) &&\tpath.splice(i, 1);\n\t\t\t}\n\t\t} else {\n\t\t\teSeg.angle = getRotationAtBezierT(eSeg, ei + eShift, 0); //record the value before we chop because it'll be impossible to determine the angle after its length is 0!\n\t\t\tei += eShift;\n\t\t\ts = eSeg[ei];\n\t\t\te = eSeg[ei+1];\n\t\t\teSeg.length = eSeg.totalLength = 0;\n\t\t\teSeg.totalPoints = path.totalPoints = 8;\n\t\t\teSeg.push(s, e, s, e, s, e, s, e);\n\t\t}\n\t}\n\tpath.totalLength = 0;\n\treturn path;\n}\n\n//measures a Segment according to its resolution (so if segment.resolution is 6, for example, it'll take 6 samples equally across each Bezier) and create/populate a \"samples\" Array that has the length up to each of those sample points (always increasing from the start) as well as a \"lookup\" array that's broken up according to the smallest distance between 2 samples. This gives us a very fast way of looking up a progress position rather than looping through all the points/Beziers. You can optionally have it only measure a subset, starting at startIndex and going for a specific number of beziers (remember, there are 3 x/y pairs each, for a total of 6 elements for each Bezier). It will also populate a \"totalLength\" property, but that's not generally super accurate because by default it'll only take 6 samples per Bezier. But for performance reasons, it's perfectly adequate for measuring progress values along the path. If you need a more accurate totalLength, either increase the resolution or use the more advanced bezierToPoints() method which keeps adding points until they don't deviate by more than a certain precision value.\nfunction measureSegment(segment, startIndex, bezierQty) {\n\tstartIndex = startIndex || 0;\n\tif (!segment.samples) {\n\t\tsegment.samples = [];\n\t\tsegment.lookup = [];\n\t}\n\tlet resolution = ~~segment.resolution || 12,\n\t\tinc = 1 / resolution,\n\t\tendIndex = bezierQty ? startIndex + bezierQty * 6 + 1 : segment.length,\n\t\tx1 = segment[startIndex],\n\t\ty1 = segment[startIndex + 1],\n\t\tsamplesIndex = startIndex ? (startIndex / 6) * resolution : 0,\n\t\tsamples = segment.samples,\n\t\tlookup = segment.lookup,\n\t\tmin = (startIndex ? segment.minLength : _largeNum) || _largeNum,\n\t\tprevLength = samples[samplesIndex + bezierQty * resolution - 1],\n\t\tlength = startIndex ? samples[samplesIndex-1] : 0,\n\t\ti, j, x4, x3, x2, xd, xd1, y4, y3, y2, yd, yd1, inv, t, lengthIndex, l, segLength;\n\tsamples.length = lookup.length = 0;\n\tfor (j = startIndex + 2; j < endIndex; j += 6) {\n\t\tx4 = segment[j + 4] - x1;\n\t\tx3 = segment[j + 2] - x1;\n\t\tx2 = segment[j] - x1;\n\t\ty4 = segment[j + 5] - y1;\n\t\ty3 = segment[j + 3] - y1;\n\t\ty2 = segment[j + 1] - y1;\n\t\txd = xd1 = yd = yd1 = 0;\n\t\tif (_abs(x4) < .01 && _abs(y4) < .01 && _abs(x2) + _abs(y2) < .01) { //dump points that are sufficiently close (basically right on top of each other, making a bezier super tiny or 0 length)\n\t\t\tif (segment.length > 8) {\n\t\t\t\tsegment.splice(j, 6);\n\t\t\t\tj -= 6;\n\t\t\t\tendIndex -= 6;\n\t\t\t}\n\t\t} else {\n\t\t\tfor (i = 1; i <= resolution; i++) {\n\t\t\t\tt = inc * i;\n\t\t\t\tinv = 1 - t;\n\t\t\t\txd = xd1 - (xd1 = (t * t * x4 + 3 * inv * (t * x3 + inv * x2)) * t);\n\t\t\t\tyd = yd1 - (yd1 = (t * t * y4 + 3 * inv * (t * y3 + inv * y2)) * t);\n\t\t\t\tl = _sqrt(yd * yd + xd * xd);\n\t\t\t\tif (l < min) {\n\t\t\t\t\tmin = l;\n\t\t\t\t}\n\t\t\t\tlength += l;\n\t\t\t\tsamples[samplesIndex++] = length;\n\t\t\t}\n\t\t}\n\t\tx1 += x4;\n\t\ty1 += y4;\n\t}\n\tif (prevLength) {\n\t\tprevLength -= length;\n\t\tfor (; samplesIndex < samples.length; samplesIndex++) {\n\t\t\tsamples[samplesIndex] += prevLength;\n\t\t}\n\t}\n\tif (samples.length && min) {\n\t\tsegment.totalLength = segLength = samples[samples.length-1] || 0;\n\t\tsegment.minLength = min;\n\t\tif (segLength / min < 9999) { // if the lookup would require too many values (memory problem), we skip this and instead we use a loop to lookup values directly in the samples Array\n\t\t\tl = lengthIndex = 0;\n\t\t\tfor (i = 0; i < segLength; i += min) {\n\t\t\t\tlookup[l++] = (samples[lengthIndex] < i) ? ++lengthIndex : lengthIndex;\n\t\t\t}\n\t\t}\n\t} else {\n\t\tsegment.totalLength = samples[0] = 0;\n\t}\n\treturn startIndex ? length - samples[startIndex / 2 - 1] : length;\n}\n\nexport function cacheRawPathMeasurements(rawPath, resolution) {\n\tlet pathLength, points, i;\n\tfor (i = pathLength = points = 0; i < rawPath.length; i++) {\n\t\trawPath[i].resolution = ~~resolution || 12; //steps per Bezier curve (anchor, 2 control points, to anchor)\n\t\tpoints += rawPath[i].length;\n\t\tpathLength += measureSegment(rawPath[i]);\n\t}\n\trawPath.totalPoints = points;\n\trawPath.totalLength = pathLength;\n\treturn rawPath;\n}\n\n//divide segment[i] at position t (value between 0 and 1, progress along that particular cubic bezier segment that starts at segment[i]). Returns how many elements were spliced into the segment array (either 0 or 6)\nexport function subdivideSegment(segment, i, t) {\n\tif (t <= 0 || t >= 1) {\n\t\treturn 0;\n\t}\n\tlet ax = segment[i],\n\t\tay = segment[i+1],\n\t\tcp1x = segment[i+2],\n\t\tcp1y = segment[i+3],\n\t\tcp2x = segment[i+4],\n\t\tcp2y = segment[i+5],\n\t\tbx = segment[i+6],\n\t\tby = segment[i+7],\n\t\tx1a = ax + (cp1x - ax) * t,\n\t\tx2 = cp1x + (cp2x - cp1x) * t,\n\t\ty1a = ay + (cp1y - ay) * t,\n\t\ty2 = cp1y + (cp2y - cp1y) * t,\n\t\tx1 = x1a + (x2 - x1a) * t,\n\t\ty1 = y1a + (y2 - y1a) * t,\n\t\tx2a = cp2x + (bx - cp2x) * t,\n\t\ty2a = cp2y + (by - cp2y) * t;\n\tx2 += (x2a - x2) * t;\n\ty2 += (y2a - y2) * t;\n\tsegment.splice(i + 2, 4,\n\t\t_round(x1a), //first control point\n\t\t_round(y1a),\n\t\t_round(x1), //second control point\n\t\t_round(y1),\n\t\t_round(x1 + (x2 - x1) * t), //new fabricated anchor on line\n\t\t_round(y1 + (y2 - y1) * t),\n\t\t_round(x2), //third control point\n\t\t_round(y2),\n\t\t_round(x2a), //fourth control point\n\t\t_round(y2a)\n\t);\n\tsegment.samples && segment.samples.splice(((i / 6) * segment.resolution) | 0, 0, 0, 0, 0, 0, 0, 0);\n\treturn 6;\n}\n\n// returns an object {path, segment, segIndex, i, t}\nfunction getProgressData(rawPath, progress, decoratee, pushToNextIfAtEnd) {\n\tdecoratee = decoratee || {};\n\trawPath.totalLength || cacheRawPathMeasurements(rawPath);\n\tif (progress < 0 || progress > 1) {\n\t\tprogress = _wrapProgress(progress);\n\t}\n\tlet segIndex = 0,\n\t\tsegment = rawPath[0],\n\t\tsamples, resolution, length, min, max, i, t;\n\tif (!progress) {\n\t\tt = i = segIndex = 0;\n\t\tsegment = rawPath[0];\n\t} else if (progress === 1) {\n\t\tt = 1;\n\t\tsegIndex = rawPath.length - 1;\n\t\tsegment = rawPath[segIndex];\n\t\ti = segment.length - 8;\n\t} else {\n\t\tif (rawPath.length > 1) { //speed optimization: most of the time, there's only one segment so skip the recursion.\n\t\t\tlength = rawPath.totalLength * progress;\n\t\t\tmax = i = 0;\n\t\t\twhile ((max += rawPath[i++].totalLength) < length) {\n\t\t\t\tsegIndex = i;\n\t\t\t}\n\t\t\tsegment = rawPath[segIndex];\n\t\t\tmin = max - segment.totalLength;\n\t\t\tprogress = ((length - min) / (max - min)) || 0;\n\t\t}\n\t\tsamples = segment.samples;\n\t\tresolution = segment.resolution; //how many samples per cubic bezier chunk\n\t\tlength = segment.totalLength * progress;\n\t\ti = segment.lookup.length ? segment.lookup[~~(length / segment.minLength)] || 0 : _getSampleIndex(samples, length, progress);\n\t\tmin = i ? samples[i-1] : 0;\n\t\tmax = samples[i];\n\t\tif (max < length) {\n\t\t\tmin = max;\n\t\t\tmax = samples[++i];\n\t\t}\n\t\tt = (1 / resolution) * (((length - min) / (max - min)) + ((i % resolution)));\n\t\ti = ~~(i / resolution) * 6;\n\t\tif (pushToNextIfAtEnd && t === 1) {\n\t\t\tif (i + 6 < segment.length) {\n\t\t\t\ti += 6;\n\t\t\t\tt = 0;\n\t\t\t} else if (segIndex + 1 < rawPath.length) {\n\t\t\t\ti = t = 0;\n\t\t\t\tsegment = rawPath[++segIndex];\n\t\t\t}\n\t\t}\n\t}\n\tdecoratee.t = t;\n\tdecoratee.i = i;\n\tdecoratee.path = rawPath;\n\tdecoratee.segment = segment;\n\tdecoratee.segIndex = segIndex;\n\treturn decoratee;\n}\n\nexport function getPositionOnPath(rawPath, progress, includeAngle, point) {\n\tlet segment = rawPath[0],\n\t\tresult = point || {},\n\t\tsamples, resolution, length, min, max, i, t, a, inv;\n\tif (progress < 0 || progress > 1) {\n\t\tprogress = _wrapProgress(progress);\n\t}\n\tsegment.lookup || cacheRawPathMeasurements(rawPath);\n\tif (rawPath.length > 1) { //speed optimization: most of the time, there's only one segment so skip the recursion.\n\t\tlength = rawPath.totalLength * progress;\n\t\tmax = i = 0;\n\t\twhile ((max += rawPath[i++].totalLength) < length) {\n\t\t\tsegment = rawPath[i];\n\t\t}\n\t\tmin = max - segment.totalLength;\n\t\tprogress = ((length - min) / (max - min)) || 0;\n\t}\n\tsamples = segment.samples;\n\tresolution = segment.resolution;\n\tlength = segment.totalLength * progress;\n\ti = segment.lookup.length ? segment.lookup[progress < 1 ? ~~(length / segment.minLength) : segment.lookup.length - 1] || 0 : _getSampleIndex(samples, length, progress);\n\tmin = i ? samples[i-1] : 0;\n\tmax = samples[i];\n\tif (max < length) {\n\t\tmin = max;\n\t\tmax = samples[++i];\n\t}\n\tt = ((1 / resolution) * (((length - min) / (max - min)) + ((i % resolution)))) || 0;\n\tinv = 1 - t;\n\ti = ~~(i / resolution) * 6;\n\ta = segment[i];\n\tresult.x = _round((t * t * (segment[i + 6] - a) + 3 * inv * (t * (segment[i + 4] - a) + inv * (segment[i + 2] - a))) * t + a);\n\tresult.y = _round((t * t * (segment[i + 7] - (a = segment[i+1])) + 3 * inv * (t * (segment[i + 5] - a) + inv * (segment[i + 3] - a))) * t + a);\n\tif (includeAngle) {\n\t\tresult.angle = segment.totalLength ? getRotationAtBezierT(segment, i, t >= 1 ? 1 - 1e-9 : t ? t : 1e-9) : segment.angle || 0;\n\t}\n\treturn result;\n}\n\n\n\n//applies a matrix transform to RawPath (or a segment in a RawPath) and returns whatever was passed in (it transforms the values in the array(s), not a copy).\nexport function transformRawPath(rawPath, a, b, c, d, tx, ty) {\n\tlet j = rawPath.length,\n\t\tsegment, l, i, x, y;\n\twhile (--j > -1) {\n\t\tsegment = rawPath[j];\n\t\tl = segment.length;\n\t\tfor (i = 0; i < l; i += 2) {\n\t\t\tx = segment[i];\n\t\t\ty = segment[i+1];\n\t\t\tsegment[i] = x * a + y * c + tx;\n\t\t\tsegment[i+1] = x * b + y * d + ty;\n\t\t}\n\t}\n\trawPath._dirty = 1;\n\treturn rawPath;\n}\n\n\n\n// translates SVG arc data into a segment (cubic beziers). Angle is in degrees.\nfunction arcToSegment(lastX, lastY, rx, ry, angle, largeArcFlag, sweepFlag, x, y) {\n\tif (lastX === x && lastY === y) {\n\t\treturn;\n\t}\n\trx = _abs(rx);\n\try = _abs(ry);\n\tlet angleRad = (angle % 360) * _DEG2RAD,\n\t\tcosAngle = _cos(angleRad),\n\t\tsinAngle = _sin(angleRad),\n\t\tPI = Math.PI,\n\t\tTWOPI = PI * 2,\n\t\tdx2 = (lastX - x) / 2,\n\t\tdy2 = (lastY - y) / 2,\n\t\tx1 = (cosAngle * dx2 + sinAngle * dy2),\n\t\ty1 = (-sinAngle * dx2 + cosAngle * dy2),\n\t\tx1_sq = x1 * x1,\n\t\ty1_sq = y1 * y1,\n\t\tradiiCheck = x1_sq / (rx * rx) + y1_sq / (ry * ry);\n\tif (radiiCheck > 1) {\n\t\trx = _sqrt(radiiCheck) * rx;\n\t\try = _sqrt(radiiCheck) * ry;\n\t}\n\tlet rx_sq = rx * rx,\n\t\try_sq = ry * ry,\n\t\tsq = ((rx_sq * ry_sq) - (rx_sq * y1_sq) - (ry_sq * x1_sq)) / ((rx_sq * y1_sq) + (ry_sq * x1_sq));\n\tif (sq < 0) {\n\t\tsq = 0;\n\t}\n\tlet coef = ((largeArcFlag === sweepFlag) ? -1 : 1) * _sqrt(sq),\n\t\tcx1 = coef * ((rx * y1) / ry),\n\t\tcy1 = coef * -((ry * x1) / rx),\n\t\tsx2 = (lastX + x) / 2,\n\t\tsy2 = (lastY + y) / 2,\n\t\tcx = sx2 + (cosAngle * cx1 - sinAngle * cy1),\n\t\tcy = sy2 + (sinAngle * cx1 + cosAngle * cy1),\n\t\tux = (x1 - cx1) / rx,\n\t\tuy = (y1 - cy1) / ry,\n\t\tvx = (-x1 - cx1) / rx,\n\t\tvy = (-y1 - cy1) / ry,\n\t\ttemp = ux * ux + uy * uy,\n\t\tangleStart = ((uy < 0) ? -1 : 1) * Math.acos(ux / _sqrt(temp)),\n\t\tangleExtent = ((ux * vy - uy * vx < 0) ? -1 : 1) * Math.acos((ux * vx + uy * vy) / _sqrt(temp * (vx * vx + vy * vy)));\n\tisNaN(angleExtent) && (angleExtent = PI); //rare edge case. Math.cos(-1) is NaN.\n\tif (!sweepFlag && angleExtent > 0) {\n\t\tangleExtent -= TWOPI;\n\t} else if (sweepFlag && angleExtent < 0) {\n\t\tangleExtent += TWOPI;\n\t}\n\tangleStart %= TWOPI;\n\tangleExtent %= TWOPI;\n\tlet segments = Math.ceil(_abs(angleExtent) / (TWOPI / 4)),\n\t\trawPath = [],\n\t\tangleIncrement = angleExtent / segments,\n\t\tcontrolLength = 4 / 3 * _sin(angleIncrement / 2) / (1 + _cos(angleIncrement / 2)),\n\t\tma = cosAngle * rx,\n\t\tmb = sinAngle * rx,\n\t\tmc = sinAngle * -ry,\n\t\tmd = cosAngle * ry,\n\t\ti;\n\tfor (i = 0; i < segments; i++) {\n\t\tangle = angleStart + i * angleIncrement;\n\t\tx1 = _cos(angle);\n\t\ty1 = _sin(angle);\n\t\tux = _cos(angle += angleIncrement);\n\t\tuy = _sin(angle);\n\t\trawPath.push(x1 - controlLength * y1, y1 + controlLength * x1, ux + controlLength * uy, uy - controlLength * ux, ux, uy);\n\t}\n\t//now transform according to the actual size of the ellipse/arc (the beziers were noramlized, between 0 and 1 on a circle).\n\tfor (i = 0; i < rawPath.length; i+=2) {\n\t\tx1 = rawPath[i];\n\t\ty1 = rawPath[i+1];\n\t\trawPath[i] = x1 * ma + y1 * mc + cx;\n\t\trawPath[i+1] = x1 * mb + y1 * md + cy;\n\t}\n\trawPath[i-2] = x; //always set the end to exactly where it's supposed to be\n\trawPath[i-1] = y;\n\treturn rawPath;\n}\n\n//Spits back a RawPath with absolute coordinates. Each segment starts with a \"moveTo\" command (x coordinate, then y) and then 2 control points (x, y, x, y), then anchor. The goal is to minimize memory and maximize speed.\nexport function stringToRawPath(d) {\n\tlet a = (d + \"\").replace(_scientific, m => { let n = +m; return (n < 0.0001 && n > -0.0001) ? 0 : n; }).match(_svgPathExp) || [], //some authoring programs spit out very small numbers in scientific notation like \"1e-5\", so make sure we round that down to 0 first.\n\t\tpath = [],\n\t\trelativeX = 0,\n\t\trelativeY = 0,\n\t\ttwoThirds = 2 / 3,\n\t\telements = a.length,\n\t\tpoints = 0,\n\t\terrorMessage = \"ERROR: malformed path: \" + d,\n\t\ti, j, x, y, command, isRelative, segment, startX, startY, difX, difY, beziers, prevCommand, flag1, flag2,\n\t\tline = function(sx, sy, ex, ey) {\n\t\t\tdifX = (ex - sx) / 3;\n\t\t\tdifY = (ey - sy) / 3;\n\t\t\tsegment.push(sx + difX, sy + difY, ex - difX, ey - difY, ex, ey);\n\t\t};\n\tif (!d || !isNaN(a[0]) || isNaN(a[1])) {\n\t\tconsole.log(errorMessage);\n\t\treturn path;\n\t}\n\tfor (i = 0; i < elements; i++) {\n\t\tprevCommand = command;\n\t\tif (isNaN(a[i])) {\n\t\t\tcommand = a[i].toUpperCase();\n\t\t\tisRelative = (command !== a[i]); //lower case means relative\n\t\t} else { //commands like \"C\" can be strung together without any new command characters between.\n\t\t\ti--;\n\t\t}\n\t\tx = +a[i + 1];\n\t\ty = +a[i + 2];\n\t\tif (isRelative) {\n\t\t\tx += relativeX;\n\t\t\ty += relativeY;\n\t\t}\n\t\tif (!i) {\n\t\t\tstartX = x;\n\t\t\tstartY = y;\n\t\t}\n\n\t\t// \"M\" (move)\n\t\tif (command === \"M\") {\n\t\t\tif (segment) {\n\t\t\t\tif (segment.length < 8) { //if the path data was funky and just had a M with no actual drawing anywhere, skip it.\n\t\t\t\t\tpath.length -= 1;\n\t\t\t\t} else {\n\t\t\t\t\tpoints += segment.length;\n\t\t\t\t}\n\t\t\t}\n\t\t\trelativeX = startX = x;\n\t\t\trelativeY = startY = y;\n\t\t\tsegment = [x, y];\n\t\t\tpath.push(segment);\n\t\t\ti += 2;\n\t\t\tcommand = \"L\"; //an \"M\" with more than 2 values gets interpreted as \"lineTo\" commands (\"L\").\n\n\t\t// \"C\" (cubic bezier)\n\t\t} else if (command === \"C\") {\n\t\t\tif (!segment) {\n\t\t\t\tsegment = [0, 0];\n\t\t\t}\n\t\t\tif (!isRelative) {\n\t\t\t\trelativeX = relativeY = 0;\n\t\t\t}\n\t\t\t//note: \"*1\" is just a fast/short way to cast the value as a Number. WAAAY faster in Chrome, slightly slower in Firefox.\n\t\t\tsegment.push(x,\ty, relativeX + a[i + 3] * 1, relativeY + a[i + 4] * 1, (relativeX += a[i + 5] * 1),\t(relativeY += a[i + 6] * 1));\n\t\t\ti += 6;\n\n\t\t// \"S\" (continuation of cubic bezier)\n\t\t} else if (command === \"S\") {\n\t\t\tdifX = relativeX;\n\t\t\tdifY = relativeY;\n\t\t\tif (prevCommand === \"C\" || prevCommand === \"S\") {\n\t\t\t\tdifX += relativeX - segment[segment.length - 4];\n\t\t\t\tdifY += relativeY - segment[segment.length - 3];\n\t\t\t}\n\t\t\tif (!isRelative) {\n\t\t\t\trelativeX = relativeY = 0;\n\t\t\t}\n\t\t\tsegment.push(difX, difY, x,\ty, (relativeX += a[i + 3] * 1), (relativeY += a[i + 4] * 1));\n\t\t\ti += 4;\n\n\t\t// \"Q\" (quadratic bezier)\n\t\t} else if (command === \"Q\") {\n\t\t\tdifX = relativeX + (x - relativeX) * twoThirds;\n\t\t\tdifY = relativeY + (y - relativeY) * twoThirds;\n\t\t\tif (!isRelative) {\n\t\t\t\trelativeX = relativeY = 0;\n\t\t\t}\n\t\t\trelativeX += a[i + 3] * 1;\n\t\t\trelativeY += a[i + 4] * 1;\n\t\t\tsegment.push(difX, difY, relativeX + (x - relativeX) * twoThirds, relativeY + (y - relativeY) * twoThirds, relativeX, relativeY);\n\t\t\ti += 4;\n\n\t\t// \"T\" (continuation of quadratic bezier)\n\t\t} else if (command === \"T\") {\n\t\t\tdifX = relativeX - segment[segment.length - 4];\n\t\t\tdifY = relativeY - segment[segment.length - 3];\n\t\t\tsegment.push(relativeX + difX, relativeY + difY, x + ((relativeX + difX * 1.5) - x) * twoThirds, y + ((relativeY + difY * 1.5) - y) * twoThirds, (relativeX = x), (relativeY = y));\n\t\t\ti += 2;\n\n\t\t// \"H\" (horizontal line)\n\t\t} else if (command === \"H\") {\n\t\t\tline(relativeX, relativeY, (relativeX = x), relativeY);\n\t\t\ti += 1;\n\n\t\t// \"V\" (vertical line)\n\t\t} else if (command === \"V\") {\n\t\t\t//adjust values because the first (and only one) isn't x in this case, it's y.\n\t\t\tline(relativeX, relativeY, relativeX, (relativeY = x + (isRelative ? relativeY - relativeX : 0)));\n\t\t\ti += 1;\n\n\t\t// \"L\" (line) or \"Z\" (close)\n\t\t} else if (command === \"L\" || command === \"Z\") {\n\t\t\tif (command === \"Z\") {\n\t\t\t\tx = startX;\n\t\t\t\ty = startY;\n\t\t\t\tsegment.closed = true;\n\t\t\t}\n\t\t\tif (command === \"L\" || _abs(relativeX - x) > 0.5 || _abs(relativeY - y) > 0.5) {\n\t\t\t\tline(relativeX, relativeY, x, y);\n\t\t\t\tif (command === \"L\") {\n\t\t\t\t\ti += 2;\n\t\t\t\t}\n\t\t\t}\n\t\t\trelativeX = x;\n\t\t\trelativeY = y;\n\n\t\t// \"A\" (arc)\n\t\t} else if (command === \"A\") {\n\t\t\tflag1 = a[i+4];\n\t\t\tflag2 = a[i+5];\n\t\t\tdifX = a[i+6];\n\t\t\tdifY = a[i+7];\n\t\t\tj = 7;\n\t\t\tif (flag1.length > 1) { // for cases when the flags are merged, like \"a8 8 0 018 8\" (the 0 and 1 flags are WITH the x value of 8, but it could also be \"a8 8 0 01-8 8\" so it may include x or not)\n\t\t\t\tif (flag1.length < 3) {\n\t\t\t\t\tdifY = difX;\n\t\t\t\t\tdifX = flag2;\n\t\t\t\t\tj--;\n\t\t\t\t} else {\n\t\t\t\t\tdifY = flag2;\n\t\t\t\t\tdifX = flag1.substr(2);\n\t\t\t\t\tj-=2;\n\t\t\t\t}\n\t\t\t\tflag2 = flag1.charAt(1);\n\t\t\t\tflag1 = flag1.charAt(0);\n\t\t\t}\n\t\t\tbeziers = arcToSegment(relativeX, relativeY, +a[i+1], +a[i+2], +a[i+3], +flag1, +flag2, (isRelative ? relativeX : 0) + difX*1, (isRelative ? relativeY : 0) + difY*1);\n\t\t\ti += j;\n\t\t\tif (beziers) {\n\t\t\t\tfor (j = 0; j < beziers.length; j++) {\n\t\t\t\t\tsegment.push(beziers[j]);\n\t\t\t\t}\n\t\t\t}\n\t\t\trelativeX = segment[segment.length-2];\n\t\t\trelativeY = segment[segment.length-1];\n\n\t\t} else {\n\t\t\tconsole.log(errorMessage);\n\t\t}\n\t}\n\ti = segment.length;\n\tif (i < 6) { //in case there's odd SVG like a M0,0 command at the very end.\n\t\tpath.pop();\n\t\ti = 0;\n\t} else if (segment[0] === segment[i-2] && segment[1] === segment[i-1]) {\n\t\tsegment.closed = true;\n\t}\n\tpath.totalPoints = points + i;\n\treturn path;\n}\n\n//populates the points array in alternating x/y values (like [x, y, x, y...] instead of individual point objects [{x, y}, {x, y}...] to conserve memory and stay in line with how we're handling segment arrays\nexport function bezierToPoints(x1, y1, x2, y2, x3, y3, x4, y4, threshold, points, index) {\n\tlet x12 = (x1 + x2) / 2,\n\t\ty12 = (y1 + y2) / 2,\n\t\tx23 = (x2 + x3) / 2,\n\t\ty23 = (y2 + y3) / 2,\n\t\tx34 = (x3 + x4) / 2,\n\t\ty34 = (y3 + y4) / 2,\n\t\tx123 = (x12 + x23) / 2,\n\t\ty123 = (y12 + y23) / 2,\n\t\tx234 = (x23 + x34) / 2,\n\t\ty234 = (y23 + y34) / 2,\n\t\tx1234 = (x123 + x234) / 2,\n\t\ty1234 = (y123 + y234) / 2,\n\t\tdx = x4 - x1,\n\t\tdy = y4 - y1,\n\t\td2 = _abs((x2 - x4) * dy - (y2 - y4) * dx),\n\t\td3 = _abs((x3 - x4) * dy - (y3 - y4) * dx),\n\t\tlength;\n\tif (!points) {\n\t\tpoints = [x1, y1, x4, y4];\n\t\tindex = 2;\n\t}\n\tpoints.splice(index || points.length - 2, 0, x1234, y1234);\n\tif ((d2 + d3) * (d2 + d3) > threshold * (dx * dx + dy * dy)) {\n\t\tlength = points.length;\n\t\tbezierToPoints(x1, y1, x12, y12, x123, y123, x1234, y1234, threshold, points, index);\n\t\tbezierToPoints(x1234, y1234, x234, y234, x34, y34, x4, y4, threshold, points, index + 2 + (points.length - length));\n\t}\n\treturn points;\n}\n\n/*\nfunction getAngleBetweenPoints(x0, y0, x1, y1, x2, y2) { //angle between 3 points in radians\n\tvar dx1 = x1 - x0,\n\t\tdy1 = y1 - y0,\n\t\tdx2 = x2 - x1,\n\t\tdy2 = y2 - y1,\n\t\tdx3 = x2 - x0,\n\t\tdy3 = y2 - y0,\n\t\ta = dx1 * dx1 + dy1 * dy1,\n\t\tb = dx2 * dx2 + dy2 * dy2,\n\t\tc = dx3 * dx3 + dy3 * dy3;\n\treturn Math.acos( (a + b - c) / _sqrt(4 * a * b) );\n},\n*/\n\n//pointsToSegment() doesn't handle flat coordinates (where y is always 0) the way we need (the resulting control points are always right on top of the anchors), so this function basically makes the control points go directly up and down, varying in length based on the curviness (more curvy, further control points)\nexport function flatPointsToSegment(points, curviness=1) {\n\tlet x = points[0],\n\t\ty = 0,\n\t\tsegment = [x, y],\n\t\ti = 2;\n\tfor (; i < points.length; i+=2) {\n\t\tsegment.push(\n\t\t\tx,\n\t\t\ty,\n\t\t\tpoints[i],\n\t\t\t(y = (points[i] - x) * curviness / 2),\n\t\t\t(x = points[i]),\n\t\t\t-y\n\t\t);\n\t}\n\treturn segment;\n}\n\n//points is an array of x/y points, like [x, y, x, y, x, y]\nexport function pointsToSegment(points, curviness) {\n\t//points = simplifyPoints(points, tolerance);\n\t_abs(points[0] - points[2]) < 1e-4 && _abs(points[1] - points[3]) < 1e-4 && (points = points.slice(2)); // if the first two points are super close, dump the first one.\n\tlet l = points.length-2,\n\t\tx = +points[0],\n\t\ty = +points[1],\n\t\tnextX = +points[2],\n\t\tnextY = +points[3],\n\t\tsegment = [x, y, x, y],\n\t\tdx2 = nextX - x,\n\t\tdy2 = nextY - y,\n\t\tclosed = Math.abs(points[l] - x) < 0.001 && Math.abs(points[l+1] - y) < 0.001,\n\t\tprevX, prevY, i, dx1, dy1, r1, r2, r3, tl, mx1, mx2, mxm, my1, my2, mym;\n\tif (closed) { // if the start and end points are basically on top of each other, close the segment by adding the 2nd point to the end, and the 2nd-to-last point to the beginning (we'll remove them at the end, but this allows the curvature to look perfect)\n\t\tpoints.push(nextX, nextY);\n\t\tnextX = x;\n\t\tnextY = y;\n\t\tx = points[l-2];\n\t\ty = points[l-1];\n\t\tpoints.unshift(x, y);\n\t\tl+=4;\n\t}\n\tcurviness = (curviness || curviness === 0) ? +curviness : 1;\n\tfor (i = 2; i < l; i+=2) {\n\t\tprevX = x;\n\t\tprevY = y;\n\t\tx = nextX;\n\t\ty = nextY;\n\t\tnextX = +points[i+2];\n\t\tnextY = +points[i+3];\n\t\tif (x === nextX && y === nextY) {\n\t\t\tcontinue;\n\t\t}\n\t\tdx1 = dx2;\n\t\tdy1 = dy2;\n\t\tdx2 = nextX - x;\n\t\tdy2 = nextY - y;\n\t\tr1 = _sqrt(dx1 * dx1 + dy1 * dy1); // r1, r2, and r3 correlate x and y (and z in the future). Basically 2D or 3D hypotenuse\n\t\tr2 = _sqrt(dx2 * dx2 + dy2 * dy2);\n\t\tr3 = _sqrt((dx2 / r2 + dx1 / r1) ** 2 + (dy2 / r2 + dy1 / r1) ** 2);\n\t\ttl = ((r1 + r2) * curviness * 0.25) / r3;\n\t\tmx1 = x - (x - prevX) * (r1 ? tl / r1 : 0);\n\t\tmx2 = x + (nextX - x) * (r2 ? tl / r2 : 0);\n\t\tmxm = x - (mx1 + (((mx2 - mx1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));\n\t\tmy1 = y - (y - prevY) * (r1 ? tl / r1 : 0);\n\t\tmy2 = y + (nextY - y) * (r2 ? tl / r2 : 0);\n\t\tmym = y - (my1 + (((my2 - my1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));\n\t\tif (x !== prevX || y !== prevY) {\n\t\t\tsegment.push(\n\t\t\t\t_round(mx1 + mxm), // first control point\n\t\t\t\t_round(my1 + mym),\n\t\t\t\t_round(x), // anchor\n\t\t\t\t_round(y),\n\t\t\t\t_round(mx2 + mxm), // second control point\n\t\t\t\t_round(my2 + mym)\n\t\t\t);\n\t\t}\n\t}\n\tx !== nextX || y !== nextY || segment.length < 4 ? segment.push(_round(nextX), _round(nextY), _round(nextX), _round(nextY)) : (segment.length -= 2);\n\tif (segment.length === 2) { // only one point!\n\t\tsegment.push(x, y, x, y, x, y);\n\t} else if (closed) {\n\t\tsegment.splice(0, 6);\n\t\tsegment.length = segment.length - 6;\n\t}\n\treturn segment;\n}\n\n//returns the squared distance between an x/y coordinate and a segment between x1/y1 and x2/y2\nfunction pointToSegDist(x, y, x1, y1, x2, y2) {\n\tlet dx = x2 - x1,\n\t\tdy = y2 - y1,\n\t\tt;\n\tif (dx || dy) {\n\t\tt = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy);\n\t\tif (t > 1) {\n\t\t\tx1 = x2;\n\t\t\ty1 = y2;\n\t\t} else if (t > 0) {\n\t\t\tx1 += dx * t;\n\t\t\ty1 += dy * t;\n\t\t}\n\t}\n\treturn (x - x1) ** 2 + (y - y1) ** 2;\n}\n\nfunction simplifyStep(points, first, last, tolerance, simplified) {\n\tlet maxSqDist = tolerance,\n\t\tfirstX = points[first],\n\t\tfirstY = points[first+1],\n\t\tlastX = points[last],\n\t\tlastY = points[last+1],\n\t\tindex, i, d;\n\tfor (i = first + 2; i < last; i += 2) {\n\t\td = pointToSegDist(points[i], points[i+1], firstX, firstY, lastX, lastY);\n\t\tif (d > maxSqDist) {\n\t\t\tindex = i;\n\t\t\tmaxSqDist = d;\n\t\t}\n\t}\n\tif (maxSqDist > tolerance) {\n\t\tindex - first > 2 && simplifyStep(points, first, index, tolerance, simplified);\n\t\tsimplified.push(points[index], points[index+1]);\n\t\tlast - index > 2 && simplifyStep(points, index, last, tolerance, simplified);\n\t}\n}\n\n//points is an array of x/y values like [x, y, x, y, x, y]\nexport function simplifyPoints(points, tolerance) {\n\tlet prevX = parseFloat(points[0]),\n\t\tprevY = parseFloat(points[1]),\n\t\ttemp = [prevX, prevY],\n\t\tl = points.length - 2,\n\t\ti, x, y, dx, dy, result, last;\n\ttolerance = (tolerance || 1) ** 2;\n\tfor (i = 2; i < l; i += 2) {\n\t\tx = parseFloat(points[i]);\n\t\ty = parseFloat(points[i+1]);\n\t\tdx = prevX - x;\n\t\tdy = prevY - y;\n\t\tif (dx * dx + dy * dy > tolerance) {\n\t\t\ttemp.push(x, y);\n\t\t\tprevX = x;\n\t\t\tprevY = y;\n\t\t}\n\t}\n\ttemp.push(parseFloat(points[l]), parseFloat(points[l+1]));\n\tlast = temp.length - 2;\n\tresult = [temp[0], temp[1]];\n\tsimplifyStep(temp, 0, last, tolerance, result);\n\tresult.push(temp[last], temp[last+1]);\n\treturn result;\n}\n\nfunction getClosestProgressOnBezier(iterations, px, py, start, end, slices, x0, y0, x1, y1, x2, y2, x3, y3) {\n\tlet inc = (end - start) / slices,\n\t\tbest = 0,\n\t\tt = start,\n\t\tx, y, d, dx, dy, inv;\n\t_bestDistance = _largeNum;\n\twhile (t <= end) {\n\t\tinv = 1 - t;\n\t\tx = inv * inv * inv * x0 + 3 * inv * inv * t * x1 + 3 * inv * t * t * x2 + t * t * t * x3;\n\t\ty = inv * inv * inv * y0 + 3 * inv * inv * t * y1 + 3 * inv * t * t * y2 + t * t * t * y3;\n\t\tdx = x - px;\n\t\tdy = y - py;\n\t\td = dx * dx + dy * dy;\n\t\tif (d < _bestDistance) {\n\t\t\t_bestDistance = d;\n\t\t\tbest = t;\n\t\t}\n\t\tt += inc;\n\t}\n\treturn (iterations > 1) ? getClosestProgressOnBezier(iterations - 1, px, py, Math.max(best - inc, 0), Math.min(best + inc, 1), slices, x0, y0, x1, y1, x2, y2, x3, y3) : best;\n}\n\nexport function getClosestData(rawPath, x, y, slices) { //returns an object with the closest j, i, and t (j is the segment index, i is the index of the point in that segment, and t is the time/progress along that bezier)\n\tlet closest = {j:0, i:0, t:0},\n\t\tbestDistance = _largeNum,\n\t\ti, j, t, segment;\n\tfor (j = 0; j < rawPath.length; j++) {\n\t\tsegment = rawPath[j];\n\t\tfor (i = 0; i < segment.length; i+=6) {\n\t\t\tt = getClosestProgressOnBezier(1, x, y, 0, 1, slices || 20, segment[i], segment[i+1], segment[i+2], segment[i+3], segment[i+4], segment[i+5], segment[i+6], segment[i+7]);\n\t\t\tif (bestDistance > _bestDistance) {\n\t\t\t\tbestDistance = _bestDistance;\n\t\t\t\tclosest.j = j;\n\t\t\t\tclosest.i = i;\n\t\t\t\tclosest.t = t;\n\t\t\t}\n\t\t}\n\t}\n\treturn closest;\n}\n\n//subdivide a Segment closest to a specific x,y coordinate\nexport function subdivideSegmentNear(x, y, segment, slices, iterations) {\n\tlet l = segment.length,\n\t\tbestDistance = _largeNum,\n\t\tbestT = 0,\n\t\tbestSegmentIndex = 0,\n\t\tt, i;\n\tslices = slices || 20;\n\titerations = iterations || 3;\n\tfor (i = 0; i < l; i += 6) {\n\t\tt = getClosestProgressOnBezier(1, x, y, 0, 1, slices, segment[i], segment[i+1], segment[i+2], segment[i+3], segment[i+4], segment[i+5], segment[i+6], segment[i+7]);\n\t\tif (bestDistance > _bestDistance) {\n\t\t\tbestDistance = _bestDistance;\n\t\t\tbestT = t;\n\t\t\tbestSegmentIndex = i;\n\t\t}\n\t}\n\tt = getClosestProgressOnBezier(iterations, x, y, bestT - 0.05, bestT + 0.05, slices, segment[bestSegmentIndex], segment[bestSegmentIndex+1], segment[bestSegmentIndex+2], segment[bestSegmentIndex+3], segment[bestSegmentIndex+4], segment[bestSegmentIndex+5], segment[bestSegmentIndex+6], segment[bestSegmentIndex+7]);\n\tsubdivideSegment(segment, bestSegmentIndex, t);\n\treturn bestSegmentIndex + 6;\n}\n\n/*\nTakes any of the following and converts it to an all Cubic Bezier SVG data string:\n- A data string like \"M0,0 L2,4 v20,15 H100\"\n- A RawPath, like [[x, y, x, y, x, y, x, y][[x, y, x, y, x, y, x, y]]\n- A Segment, like [x, y, x, y, x, y, x, y]\n\nNote: all numbers are rounded down to the closest 0.001 to minimize memory, maximize speed, and avoid odd numbers like 1e-13\n*/\nexport function rawPathToString(rawPath) {\n\tif (_isNumber(rawPath[0])) { //in case a segment is passed in instead\n\t\trawPath = [rawPath];\n\t}\n\tlet result = \"\",\n\t\tl = rawPath.length,\n\t\tsl, s, i, segment;\n\tfor (s = 0; s < l; s++) {\n\t\tsegment = rawPath[s];\n\t\tresult += \"M\" + _round(segment[0]) + \",\" + _round(segment[1]) + \" C\";\n\t\tsl = segment.length;\n\t\tfor (i = 2; i < sl; i++) {\n\t\t\tresult += _round(segment[i++]) + \",\" + _round(segment[i++]) + \" \" + _round(segment[i++]) + \",\" + _round(segment[i++]) + \" \" + _round(segment[i++]) + \",\" + _round(segment[i]) + \" \";\n\t\t}\n\t\tif (segment.closed) {\n\t\t\tresult += \"z\";\n\t\t}\n\t}\n\treturn result;\n}\n\n/*\n// takes a segment with coordinates [x, y, x, y, ...] and converts the control points into angles and lengths [x, y, angle, length, angle, length, x, y, angle, length, ...] so that it animates more cleanly and avoids odd breaks/kinks. For example, if you animate from 1 o'clock to 6 o'clock, it'd just go directly/linearly rather than around. So the length would be very short in the middle of the tween.\nexport function cpCoordsToAngles(segment, copy) {\n\tvar result = copy ? segment.slice(0) : segment,\n\t\tx, y, i;\n\tfor (i = 0; i < segment.length; i+=6) {\n\t\tx = segment[i+2] - segment[i];\n\t\ty = segment[i+3] - segment[i+1];\n\t\tresult[i+2] = Math.atan2(y, x);\n\t\tresult[i+3] = Math.sqrt(x * x + y * y);\n\t\tx = segment[i+6] - segment[i+4];\n\t\ty = segment[i+7] - segment[i+5];\n\t\tresult[i+4] = Math.atan2(y, x);\n\t\tresult[i+5] = Math.sqrt(x * x + y * y);\n\t}\n\treturn result;\n}\n\n// takes a segment that was converted with cpCoordsToAngles() to have angles and lengths instead of coordinates for the control points, and converts it BACK into coordinates.\nexport function cpAnglesToCoords(segment, copy) {\n\tvar result = copy ? segment.slice(0) : segment,\n\t\tlength = segment.length,\n\t\trnd = 1000,\n\t\tangle, l, i, j;\n\tfor (i = 0; i < length; i+=6) {\n\t\tangle = segment[i+2];\n\t\tl = segment[i+3]; //length\n\t\tresult[i+2] = (((segment[i] + Math.cos(angle) * l) * rnd) | 0) / rnd;\n\t\tresult[i+3] = (((segment[i+1] + Math.sin(angle) * l) * rnd) | 0) / rnd;\n\t\tangle = segment[i+4];\n\t\tl = segment[i+5]; //length\n\t\tresult[i+4] = (((segment[i+6] - Math.cos(angle) * l) * rnd) | 0) / rnd;\n\t\tresult[i+5] = (((segment[i+7] - Math.sin(angle) * l) * rnd) | 0) / rnd;\n\t}\n\treturn result;\n}\n\n//adds an \"isSmooth\" array to each segment and populates it with a boolean value indicating whether or not it's smooth (the control points have basically the same slope). For any smooth control points, it converts the coordinates into angle (x, in radians) and length (y) and puts them into the same index value in a smoothData array.\nexport function populateSmoothData(rawPath) {\n\tlet j = rawPath.length,\n\t\tsmooth, segment, x, y, x2, y2, i, l, a, a2, isSmooth, smoothData;\n\twhile (--j > -1) {\n\t\tsegment = rawPath[j];\n\t\tisSmooth = segment.isSmooth = segment.isSmooth || [0, 0, 0, 0];\n\t\tsmoothData = segment.smoothData = segment.smoothData || [0, 0, 0, 0];\n\t\tisSmooth.length = 4;\n\t\tl = segment.length - 2;\n\t\tfor (i = 6; i < l; i += 6) {\n\t\t\tx = segment[i] - segment[i - 2];\n\t\t\ty = segment[i + 1] - segment[i - 1];\n\t\t\tx2 = segment[i + 2] - segment[i];\n\t\t\ty2 = segment[i + 3] - segment[i + 1];\n\t\t\ta = _atan2(y, x);\n\t\t\ta2 = _atan2(y2, x2);\n\t\t\tsmooth = (Math.abs(a - a2) < 0.09);\n\t\t\tif (smooth) {\n\t\t\t\tsmoothData[i - 2] = a;\n\t\t\t\tsmoothData[i + 2] = a2;\n\t\t\t\tsmoothData[i - 1] = _sqrt(x * x + y * y);\n\t\t\t\tsmoothData[i + 3] = _sqrt(x2 * x2 + y2 * y2);\n\t\t\t}\n\t\t\tisSmooth.push(smooth, smooth, 0, 0, smooth, smooth);\n\t\t}\n\t\t//if the first and last points are identical, check to see if there's a smooth transition. We must handle this a bit differently due to their positions in the array.\n\t\tif (segment[l] === segment[0] && segment[l+1] === segment[1]) {\n\t\t\tx = segment[0] - segment[l-2];\n\t\t\ty = segment[1] - segment[l-1];\n\t\t\tx2 = segment[2] - segment[0];\n\t\t\ty2 = segment[3] - segment[1];\n\t\t\ta = _atan2(y, x);\n\t\t\ta2 = _atan2(y2, x2);\n\t\t\tif (Math.abs(a - a2) < 0.09) {\n\t\t\t\tsmoothData[l-2] = a;\n\t\t\t\tsmoothData[2] = a2;\n\t\t\t\tsmoothData[l-1] = _sqrt(x * x + y * y);\n\t\t\t\tsmoothData[3] = _sqrt(x2 * x2 + y2 * y2);\n\t\t\t\tisSmooth[l-2] = isSmooth[l-1] = true; //don't change indexes 2 and 3 because we'll trigger everything from the END, and this will optimize file size a bit.\n\t\t\t}\n\t\t}\n\t}\n\treturn rawPath;\n}\nexport function pointToScreen(svgElement, point) {\n\tif (arguments.length < 2) { //by default, take the first set of coordinates in the path as the point\n\t\tlet rawPath = getRawPath(svgElement);\n\t\tpoint = svgElement.ownerSVGElement.createSVGPoint();\n\t\tpoint.x = rawPath[0][0];\n\t\tpoint.y = rawPath[0][1];\n\t}\n\treturn point.matrixTransform(svgElement.getScreenCTM());\n}\n// takes a and normalizes all of its coordinates to values between 0 and 1\nexport function normalizePath(path) {\n path = gsap.utils.toArray(path);\n if (!path[0].hasAttribute(\"d\")) {\n path = gsap.utils.toArray(path[0].children);\n }\n if (path.length > 1) {\n path.forEach(normalizePath);\n return path;\n }\n let _svgPathExp = /[achlmqstvz]|(-?\\d*\\.?\\d*(?:e[\\-+]?\\d+)?)[0-9]/ig,\n _scientific = /[\\+\\-]?\\d*\\.?\\d+e[\\+\\-]?\\d+/ig,\n d = path[0].getAttribute(\"d\"),\n a = d.replace(_scientific, m => { let n = +m; return (n < 0.0001 && n > -0.0001) ? 0 : n; }).match(_svgPathExp),\n nums = a.filter(n => !isNaN(n)).map(n => +n),\n normalize = gsap.utils.normalize(Math.min(...nums), Math.max(...nums)),\n finals = a.map(val => isNaN(val) ? val : normalize(+val)),\n s = \"\",\n prevWasCommand;\n finals.forEach((value, i) => {\n let isCommand = isNaN(value)\n s += (isCommand && i ? \" \" : prevWasCommand || !i ? \"\" : \",\") + value;\n prevWasCommand = isCommand;\n });\n path[0].setAttribute(\"d\", s);\n}\n*/","/*!\n * CustomEase 3.13.0\n * https://gsap.com\n *\n * @license Copyright 2008-2025, GreenSock. All rights reserved.\n * Subject to the terms at https://gsap.com/standard-license\n * @author: Jack Doyle, jack@greensock.com\n*/\n/* eslint-disable */\n\nimport { stringToRawPath, rawPathToString, transformRawPath } from \"./utils/paths.js\";\n\nlet gsap, _coreInitted,\n\t_getGSAP = () => gsap || (typeof(window) !== \"undefined\" && (gsap = window.gsap) && gsap.registerPlugin && gsap),\n\t_initCore = () => {\n\t\tgsap = _getGSAP();\n\t\tif (gsap) {\n\t\t\tgsap.registerEase(\"_CE\", CustomEase.create);\n\t\t\t_coreInitted = 1;\n\t\t} else {\n\t\t\tconsole.warn(\"Please gsap.registerPlugin(CustomEase)\");\n\t\t}\n\t},\n\t_bigNum = 1e20,\n\t_round = value => ~~(value * 1000 + (value < 0 ? -.5 : .5)) / 1000,\n\t_bonusValidated = 1, //CustomEase\n\t_numExp = /[-+=.]*\\d+[.e\\-+]*\\d*[e\\-+]*\\d*/gi, //finds any numbers, including ones that start with += or -=, negative numbers, and ones in scientific notation like 1e-8.\n\t_needsParsingExp = /[cLlsSaAhHvVtTqQ]/g,\n\t_findMinimum = values => {\n\t\tlet l = values.length,\n\t\t\tmin = _bigNum,\n\t\t\ti;\n\t\tfor (i = 1; i < l; i += 6) {\n\t\t\t+values[i] < min && (min = +values[i]);\n\t\t}\n\t\treturn min;\n\t},\n\t//takes all the points and translates/scales them so that the x starts at 0 and ends at 1.\n\t_normalize = (values, height, originY) => {\n\t\tif (!originY && originY !== 0) {\n\t\t\toriginY = Math.max(+values[values.length-1], +values[1]);\n\t\t}\n\t\tlet tx = +values[0] * -1,\n\t\t\tty = -originY,\n\t\t\tl = values.length,\n\t\t\tsx = 1 / (+values[l - 2] + tx),\n\t\t\tsy = -height || ((Math.abs(+values[l - 1] - +values[1]) < 0.01 * (+values[l - 2] - +values[0])) ? _findMinimum(values) + ty : +values[l - 1] + ty),\n\t\t\ti;\n\t\tif (sy) { //typically y ends at 1 (so that the end values are reached)\n\t\t\tsy = 1 / sy;\n\t\t} else { //in case the ease returns to its beginning value, scale everything proportionally\n\t\t\tsy = -sx;\n\t\t}\n\t\tfor (i = 0; i < l; i += 2) {\n\t\t\tvalues[i] = (+values[i] + tx) * sx;\n\t\t\tvalues[i + 1] = (+values[i + 1] + ty) * sy;\n\t\t}\n\t},\n\t//note that this function returns point objects like {x, y} rather than working with segments which are arrays with alternating x, y values as in the similar function in paths.js\n\t_bezierToPoints = function (x1, y1, x2, y2, x3, y3, x4, y4, threshold, points, index) {\n\t\tlet x12 = (x1 + x2) / 2,\n\t\t\ty12 = (y1 + y2) / 2,\n\t\t\tx23 = (x2 + x3) / 2,\n\t\t\ty23 = (y2 + y3) / 2,\n\t\t\tx34 = (x3 + x4) / 2,\n\t\t\ty34 = (y3 + y4) / 2,\n\t\t\tx123 = (x12 + x23) / 2,\n\t\t\ty123 = (y12 + y23) / 2,\n\t\t\tx234 = (x23 + x34) / 2,\n\t\t\ty234 = (y23 + y34) / 2,\n\t\t\tx1234 = (x123 + x234) / 2,\n\t\t\ty1234 = (y123 + y234) / 2,\n\t\t\tdx = x4 - x1,\n\t\t\tdy = y4 - y1,\n\t\t\td2 = Math.abs((x2 - x4) * dy - (y2 - y4) * dx),\n\t\t\td3 = Math.abs((x3 - x4) * dy - (y3 - y4) * dx),\n\t\t\tlength;\n\t\tif (!points) {\n\t\t\tpoints = [{x: x1, y: y1}, {x: x4, y: y4}];\n\t\t\tindex = 1;\n\t\t}\n\t\tpoints.splice(index || points.length - 1, 0, {x: x1234, y: y1234});\n\t\tif ((d2 + d3) * (d2 + d3) > threshold * (dx * dx + dy * dy)) {\n\t\t\tlength = points.length;\n\t\t\t_bezierToPoints(x1, y1, x12, y12, x123, y123, x1234, y1234, threshold, points, index);\n\t\t\t_bezierToPoints(x1234, y1234, x234, y234, x34, y34, x4, y4, threshold, points, index + 1 + (points.length - length));\n\t\t}\n\t\treturn points;\n\t};\n\nexport class CustomEase {\n\n\tconstructor(id, data, config) {\n\t\t_coreInitted || _initCore();\n\t\tthis.id = id;\n\t\t_bonusValidated && this.setData(data, config);\n\t}\n\n\tsetData(data, config) {\n\t\tconfig = config || {};\n\t\tdata = data || \"0,0,1,1\";\n\t\tlet values = data.match(_numExp),\n\t\t\tclosest = 1,\n\t\t\tpoints = [],\n\t\t\tlookup = [],\n\t\t\tprecision = config.precision || 1,\n\t\t\tfast = (precision <= 1),\n\t\t\tl, a1, a2, i, inc, j, point, prevPoint, p;\n\t\tthis.data = data;\n\t\tif (_needsParsingExp.test(data) || (~data.indexOf(\"M\") && data.indexOf(\"C\") < 0)) {\n\t\t\tvalues = stringToRawPath(data)[0];\n\t\t}\n\t\tl = values.length;\n\t\tif (l === 4) {\n\t\t\tvalues.unshift(0, 0);\n\t\t\tvalues.push(1, 1);\n\t\t\tl = 8;\n\t\t} else if ((l - 2) % 6) {\n\t\t\tthrow \"Invalid CustomEase\";\n\t\t}\n\t\tif (+values[0] !== 0 || +values[l - 2] !== 1) {\n\t\t\t_normalize(values, config.height, config.originY);\n\t\t}\n\t\tthis.segment = values;\n\t\tfor (i = 2; i < l; i += 6) {\n\t\t\ta1 = {x: +values[i - 2], y: +values[i - 1]};\n\t\t\ta2 = {x: +values[i + 4], y: +values[i + 5]};\n\t\t\tpoints.push(a1, a2);\n\t\t\t_bezierToPoints(a1.x, a1.y, +values[i], +values[i + 1], +values[i + 2], +values[i + 3], a2.x, a2.y, 1 / (precision * 200000), points, points.length - 1);\n\t\t}\n\t\tl = points.length;\n\t\tfor (i = 0; i < l; i++) {\n\t\t\tpoint = points[i];\n\t\t\tprevPoint = points[i - 1] || point;\n\t\t\tif ((point.x > prevPoint.x || (prevPoint.y !== point.y && prevPoint.x === point.x) || point === prevPoint) && point.x <= 1) { //if a point goes BACKWARD in time or is a duplicate, just drop it. Also it shouldn't go past 1 on the x axis, as could happen in a string like \"M0,0 C0,0 0.12,0.68 0.18,0.788 0.195,0.845 0.308,1 0.32,1 0.403,1.005 0.398,1 0.5,1 0.602,1 0.816,1.005 0.9,1 0.91,1 0.948,0.69 0.962,0.615 1.003,0.376 1,0 1,0\".\n\t\t\t\tprevPoint.cx = point.x - prevPoint.x; //change in x between this point and the next point (performance optimization)\n\t\t\t\tprevPoint.cy = point.y - prevPoint.y;\n\t\t\t\tprevPoint.n = point;\n\t\t\t\tprevPoint.nx = point.x; //next point's x value (performance optimization, making lookups faster in getRatio()). Remember, the lookup will always land on a spot where it's either this point or the very next one (never beyond that)\n\t\t\t\tif (fast && i > 1 && Math.abs(prevPoint.cy / prevPoint.cx - points[i - 2].cy / points[i - 2].cx) > 2) { //if there's a sudden change in direction, prioritize accuracy over speed. Like a bounce ease - you don't want to risk the sampling chunks landing on each side of the bounce anchor and having it clipped off.\n\t\t\t\t\tfast = 0;\n\t\t\t\t}\n\t\t\t\tif (prevPoint.cx < closest) {\n\t\t\t\t\tif (!prevPoint.cx) {\n\t\t\t\t\t\tprevPoint.cx = 0.001; //avoids math problems in getRatio() (dividing by zero)\n\t\t\t\t\t\tif (i === l - 1) { //in case the final segment goes vertical RIGHT at the end, make sure we end at the end.\n\t\t\t\t\t\t\tprevPoint.x -= 0.001;\n\t\t\t\t\t\t\tclosest = Math.min(closest, 0.001);\n\t\t\t\t\t\t\tfast = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tclosest = prevPoint.cx;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tpoints.splice(i--, 1);\n\t\t\t\tl--;\n\t\t\t}\n\t\t}\n\t\tl = (1 / closest + 1) | 0;\n\t\tinc = 1 / l;\n\t\tj = 0;\n\t\tpoint = points[0];\n\t\tif (fast) {\n\t\t\tfor (i = 0; i < l; i++) { //for fastest lookups, we just sample along the path at equal x (time) distance. Uses more memory and is slightly less accurate for anchors that don't land on the sampling points, but for the vast majority of eases it's excellent (and fast).\n\t\t\t\tp = i * inc;\n\t\t\t\tif (point.nx < p) {\n\t\t\t\t\tpoint = points[++j];\n\t\t\t\t}\n\t\t\t\ta1 = point.y + ((p - point.x) / point.cx) * point.cy;\n\t\t\t\tlookup[i] = {x: p, cx: inc, y: a1, cy: 0, nx: 9};\n\t\t\t\tif (i) {\n\t\t\t\t\tlookup[i - 1].cy = a1 - lookup[i - 1].y;\n\t\t\t\t}\n\t\t\t}\n\t\t\tj = points[points.length - 1];\n\t\t\tlookup[l - 1].cy = j.y - a1;\n\t\t\tlookup[l - 1].cx = j.x - lookup[lookup.length - 1].x; //make sure it lands EXACTLY where it should. Otherwise, it might be something like 0.9999999999 instead of 1.\n\t\t} else { //this option is more accurate, ensuring that EVERY anchor is hit perfectly. Clipping across a bounce, for example, would never happen.\n\t\t\tfor (i = 0; i < l; i++) { //build a lookup table based on the smallest distance so that we can instantly find the appropriate point (well, it'll either be that point or the very next one). We'll look up based on the linear progress. So it's it's 0.5 and the lookup table has 100 elements, it'd be like lookup[Math.floor(0.5 * 100)]\n\t\t\t\tif (point.nx < i * inc) {\n\t\t\t\t\tpoint = points[++j];\n\t\t\t\t}\n\t\t\t\tlookup[i] = point;\n\t\t\t}\n\n\t\t\tif (j < points.length - 1) {\n\t\t\t\tlookup[i-1] = points[points.length-2];\n\t\t\t}\n\t\t}\n\t\t//this._calcEnd = (points[points.length-1].y !== 1 || points[0].y !== 0); //ensures that we don't run into floating point errors. As long as we're starting at 0 and ending at 1, tell GSAP to skip the final calculation and use 0/1 as the factor.\n\n\t\tthis.ease = p => {\n\t\t\tlet point = lookup[(p * l) | 0] || lookup[l - 1];\n\t\t\tif (point.nx < p) {\n\t\t\t\tpoint = point.n;\n\t\t\t}\n\t\t\treturn point.y + ((p - point.x) / point.cx) * point.cy;\n\t\t};\n\n\t\tthis.ease.custom = this;\n\n\t\tthis.id && gsap && gsap.registerEase(this.id, this.ease);\n\n\t\treturn this;\n\t}\n\n\tgetSVGData(config) {\n\t\treturn CustomEase.getSVGData(this, config);\n\t}\n\n\tstatic create(id, data, config) {\n\t\treturn (new CustomEase(id, data, config)).ease;\n\t}\n\n\tstatic register(core) {\n\t\tgsap = core;\n\t\t_initCore();\n\t}\n\n\tstatic get(id) {\n\t\treturn gsap.parseEase(id);\n\t}\n\n\tstatic getSVGData(ease, config) {\n\t\tconfig = config || {};\n\t\tlet width = config.width || 100,\n\t\t\theight = config.height || 100,\n\t\t\tx = config.x || 0,\n\t\t\ty = (config.y || 0) + height,\n\t\t\te = gsap.utils.toArray(config.path)[0],\n\t\t\ta, slope, i, inc, tx, ty, precision, threshold, prevX, prevY;\n\t\tif (config.invert) {\n\t\t\theight = -height;\n\t\t\ty = 0;\n\t\t}\n\t\tif (typeof(ease) === \"string\") {\n\t\t\tease = gsap.parseEase(ease);\n\t\t}\n\t\tif (ease.custom) {\n\t\t\tease = ease.custom;\n\t\t}\n\t\tif (ease instanceof CustomEase) {\n\t\t\ta = rawPathToString(transformRawPath([ease.segment], width, 0, 0, -height, x, y));\n\t\t} else {\n\t\t\ta = [x, y];\n\t\t\tprecision = Math.max(5, (config.precision || 1) * 200);\n\t\t\tinc = 1 / precision;\n\t\t\tprecision += 2;\n\t\t\tthreshold = 5 / precision;\n\t\t\tprevX = _round(x + inc * width);\n\t\t\tprevY = _round(y + ease(inc) * -height);\n\t\t\tslope = (prevY - y) / (prevX - x);\n\t\t\tfor (i = 2; i < precision; i++) {\n\t\t\t\ttx = _round(x + i * inc * width);\n\t\t\t\tty = _round(y + ease(i * inc) * -height);\n\t\t\t\tif (Math.abs((ty - prevY) / (tx - prevX) - slope) > threshold || i === precision - 1) { //only add points when the slope changes beyond the threshold\n\t\t\t\t\ta.push(prevX, prevY);\n\t\t\t\t\tslope = (ty - prevY) / (tx - prevX);\n\t\t\t\t}\n\t\t\t\tprevX = tx;\n\t\t\t\tprevY = ty;\n\t\t\t}\n\t\t\ta = \"M\" + a.join(\",\");\n\t\t}\n\t\te && e.setAttribute(\"d\", a);\n\t\treturn a;\n\t}\n\n}\n\nCustomEase.version = \"3.13.0\";\nCustomEase.headless = true;\n\n_getGSAP() && gsap.registerPlugin(CustomEase);\n\nexport { CustomEase as default };"],"names":["_round","value","Math","round","_svgPathExp","_scientific","_DEG2RAD","PI","_sin","sin","_cos","cos","_abs","abs","_sqrt","sqrt","arcToSegment","lastX","lastY","rx","ry","angle","largeArcFlag","sweepFlag","x","y","angleRad","cosAngle","sinAngle","TWOPI","dx2","dy2","x1","y1","x1_sq","y1_sq","radiiCheck","rx_sq","ry_sq","sq","coef","cx1","cy1","cx","cy","ux","uy","vx","vy","temp","angleStart","acos","angleExtent","isNaN","i","segments","ceil","rawPath","angleIncrement","controlLength","ma","mb","mc","md","push","length","stringToRawPath","d","line","sx","sy","ex","ey","difX","difY","segment","j","command","isRelative","startX","startY","beziers","prevCommand","flag1","flag2","a","replace","m","n","match","path","relativeX","relativeY","elements","points","errorMessage","console","log","toUpperCase","closed","substr","charAt","pop","totalPoints","_getGSAP","gsap","window","registerPlugin","_initCore","registerEase","CustomEase","create","_coreInitted","warn","_bezierToPoints","x2","y2","x3","y3","x4","y4","threshold","index","x12","y12","x23","y23","x34","y34","x123","y123","x234","y234","x1234","y1234","dx","dy","d2","d3","splice","_bonusValidated","_needsParsingExp","setData","data","config","l","a1","a2","inc","point","prevPoint","p","values","_numExp","closest","lookup","precision","fast","test","indexOf","unshift","_normalize","height","originY","max","tx","ty","_findMinimum","min","nx","ease","custom","this","id","getSVGData","register","core","get","parseEase","slope","prevX","prevY","width","e","utils","toArray","invert","rawPathToString","_isNumber","sl","s","result","transformRawPath","b","c","_dirty","join","setAttribute","version","headless"],"mappings":"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"} \ No newline at end of file diff --git a/vendor/javascript/gsap/CustomWiggle.min.js b/vendor/javascript/gsap/CustomWiggle.min.js new file mode 100644 index 0000000..6ffb98e --- /dev/null +++ b/vendor/javascript/gsap/CustomWiggle.min.js @@ -0,0 +1,11 @@ +/*! + * CustomWiggle 3.13.0 + * https://gsap.com + * + * @license Copyright 2025, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function g(){return n||"undefined"!=typeof window&&(n=window.gsap)&&n.registerPlugin&&n}function i(e){return e}function j(e){if(!C)if(n=g(),M=n&&n.parseEase("_CE")){for(var t in y)y[t]=M("",y[t]);C=1,o("wiggle").config=function(e){return"object"==typeof e?o("",e):o("wiggle("+e+")",{wiggles:+e})}}else e&&console.warn("Please gsap.registerPlugin(CustomEase, CustomWiggle)")}function k(t,e){return"function"!=typeof t&&(t=n.parseEase(t)||M("",t)),t.custom||!e?t:function(e){return 1-t(e)}}var n,C,M,y={easeOut:"M0,1,C0.7,1,0.6,0,1,0",easeInOut:"M0,0,C0.1,0,0.24,1,0.444,1,0.644,1,0.6,0,1,0",anticipate:"M0,0,C0,0.222,0.024,0.386,0,0.4,0.18,0.455,0.65,0.646,0.7,0.67,0.9,0.76,1,0.846,1,1",uniform:"M0,0,C0,0.95,0,1,0,1,0,1,1,1,1,1,1,1,1,0,1,0"},o=function _create(e,t){C||j(1);var n,o,s,u,r,a,g,f,l,c=0|((t=t||{}).wiggles||10),p=1/c,d=p/2,m="anticipate"===t.type,h=y[t.type]||y.easeOut,w=i;if(m&&(w=h,h=y.easeOut),t.timingEase&&(w=k(t.timingEase)),t.amplitudeEase&&(h=k(t.amplitudeEase,!0)),f=[0,0,(a=w(d))/4,0,a/2,g=m?-h(d):h(d),a,g],"random"===t.type){for(f.length=4,n=w(p),o=2*Math.random()-1,l=2;l gsap || (typeof(window) !== \"undefined\" && (gsap = window.gsap) && gsap.registerPlugin && gsap),\n\t_eases = {\n\t\teaseOut: \"M0,1,C0.7,1,0.6,0,1,0\",\n\t\teaseInOut: \"M0,0,C0.1,0,0.24,1,0.444,1,0.644,1,0.6,0,1,0\",\n\t\tanticipate: \"M0,0,C0,0.222,0.024,0.386,0,0.4,0.18,0.455,0.65,0.646,0.7,0.67,0.9,0.76,1,0.846,1,1\",\n\t\tuniform: \"M0,0,C0,0.95,0,1,0,1,0,1,1,1,1,1,1,1,1,0,1,0\"\n\t},\n\t_linearEase = p => p,\n\t_initCore = required => {\n\t\tif (!_coreInitted) {\n\t\t\tgsap = _getGSAP();\n\t\t\tcreateCustomEase = gsap && gsap.parseEase(\"_CE\");\n\t\t\tif (createCustomEase) {\n\t\t\t\tfor (let p in _eases) {\n\t\t\t\t\t_eases[p] = createCustomEase(\"\", _eases[p]);\n\t\t\t\t}\n\t\t\t\t_coreInitted = 1;\n\t\t\t\t_create(\"wiggle\").config = vars => typeof(vars) === \"object\" ? _create(\"\", vars) : _create(\"wiggle(\" + vars + \")\", {wiggles:+vars});\n\t\t\t} else {\n\t\t\t\trequired && console.warn(\"Please gsap.registerPlugin(CustomEase, CustomWiggle)\");\n\t\t\t}\n\t\t}\n\t},\n\t_parseEase = (ease, invertNonCustomEases) => {\n\t\tif (typeof(ease) !== \"function\") {\n\t\t\tease = gsap.parseEase(ease) || createCustomEase(\"\", ease);\n\t\t}\n\t\treturn (ease.custom || !invertNonCustomEases) ? ease : p => 1 - ease(p);\n\t},\n\t_bonusValidated = 1, //CustomWiggle\n\t_create = (id, vars) => {\n\t\tif (!_coreInitted) {\n\t\t\t_initCore(1);\n\t\t}\n\t\tvars = vars || {};\n\t\tlet wiggles = (vars.wiggles || 10) | 0,\n\t\t\tinc = 1 / wiggles,\n\t\t\tx = inc / 2,\n\t\t\tanticipate = (vars.type === \"anticipate\"),\n\t\t\tyEase = _eases[vars.type] || _eases.easeOut,\n\t\t\txEase = _linearEase,\n\t\t\trnd = 1000,\n\t\t\tnextX, nextY, angle, handleX, handleY, easedX, y, path, i;\n\t\tif (_bonusValidated) {\n\t\t\tif (anticipate) { //the anticipate ease is actually applied on the x-axis (timing) and uses easeOut for amplitude.\n\t\t\t\txEase = yEase;\n\t\t\t\tyEase = _eases.easeOut;\n\t\t\t}\n\t\t\tif (vars.timingEase) {\n\t\t\t\txEase = _parseEase(vars.timingEase);\n\t\t\t}\n\t\t\tif (vars.amplitudeEase) {\n\t\t\t\tyEase = _parseEase(vars.amplitudeEase, true);\n\t\t\t}\n\t\t\teasedX = xEase(x);\n\t\t\ty = anticipate ? -yEase(x) : yEase(x);\n\t\t\tpath = [0, 0, easedX / 4, 0, easedX / 2, y, easedX, y];\n\n\t\t\tif (vars.type === \"random\") { //if we just select random values on the y-axis and plug them into the \"normal\" algorithm, since the control points are always straight horizontal, it creates a bit of a slowdown at each anchor which just didn't seem as desirable, so we switched to an algorithm that bends the control points to be more in line with their context.\n\t\t\t\tpath.length = 4;\n\t\t\t\tnextX = xEase(inc);\n\t\t\t\tnextY = Math.random() * 2 - 1;\n\t\t\t\tfor (i = 2; i < wiggles; i++) {\n\t\t\t\t\tx = nextX;\n\t\t\t\t\ty = nextY;\n\t\t\t\t\tnextX = xEase(inc * i);\n\t\t\t\t\tnextY = Math.random() * 2 - 1;\n\t\t\t\t\tangle = Math.atan2(nextY - path[path.length - 3], nextX - path[path.length - 4]);\n\t\t\t\t\thandleX = Math.cos(angle) * inc;\n\t\t\t\t\thandleY = Math.sin(angle) * inc;\n\t\t\t\t\tpath.push(x - handleX, y - handleY, x, y, x + handleX, y + handleY);\n\t\t\t\t}\n\t\t\t\tpath.push(nextX, 0, 1, 0);\n\t\t\t} else {\n\t\t\t\tfor (i = 1; i < wiggles; i++) {\n\t\t\t\t\tpath.push(xEase(x + inc / 2), y);\n\t\t\t\t\tx += inc;\n\t\t\t\t\ty = ((y > 0) ? -1 : 1) * (yEase(i * inc));\n\t\t\t\t\teasedX = xEase(x);\n\t\t\t\t\tpath.push(xEase(x - inc / 2), y, easedX, y);\n\t\t\t\t}\n\t\t\t\tpath.push(xEase(x + inc / 4), y, xEase(x + inc / 4), 0, 1, 0);\n\t\t\t}\n\t\t\ti = path.length;\n\t\t\twhile (--i > -1) {\n\t\t\t\tpath[i] = ~~(path[i] * rnd) / rnd; //round values to avoid odd strings for super tiny values\n\t\t\t}\n\t\t\tpath[2] = \"C\" + path[2];\n\t\t\treturn createCustomEase(id, \"M\" + path.join(\",\"));\n\t\t}\n\t};\n\nexport class CustomWiggle {\n\n\tconstructor(id, vars) {\n\t\tthis.ease = _create(id, vars);\n\t}\n\n\tstatic create(id, vars) {\n\t\treturn _create(id, vars);\n\t}\n\n\tstatic register(core) {\n\t\tgsap = core;\n\t\t_initCore();\n\t}\n\n}\n\n_getGSAP() && gsap.registerPlugin(CustomWiggle);\n\nCustomWiggle.version = \"3.13.0\";\n\nexport { CustomWiggle as default };"],"names":["_getGSAP","gsap","window","registerPlugin","_linearEase","p","_initCore","required","_coreInitted","createCustomEase","parseEase","_eases","_create","config","vars","wiggles","console","warn","_parseEase","ease","invertNonCustomEases","custom","easeOut","easeInOut","anticipate","uniform","id","nextX","nextY","angle","handleX","handleY","easedX","y","path","i","inc","x","type","yEase","xEase","timingEase","amplitudeEase","length","Math","random","atan2","cos","sin","push","join","CustomWiggle","create","register","core","version"],"mappings":"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"} \ No newline at end of file diff --git a/vendor/javascript/gsap/Draggable.min.js b/vendor/javascript/gsap/Draggable.min.js new file mode 100644 index 0000000..cec0540 --- /dev/null +++ b/vendor/javascript/gsap/Draggable.min.js @@ -0,0 +1,11 @@ +/*! + * Draggable 3.13.0 + * https://gsap.com + * + * @license Copyright 2025, GreenSock. All rights reserved. + * Subject to the terms at https://gsap.com/standard-license. + * @author: Jack Doyle, jack@greensock.com + */ + +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).window=e.window||{})}(this,function(e){"use strict";function _assertThisInitialized(e){if(void 0===e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return e}function w(e,t){if(e.parentNode&&(h||M(e))){var n=P(e),o=n?n.getAttribute("xmlns")||"http://www.w3.org/2000/svg":"http://www.w3.org/1999/xhtml",r=n?t?"rect":"g":"div",i=2!==t?0:100,a=3===t?100:0,l="position:absolute;display:block;pointer-events:none;margin:0;padding:0;",s=h.createElementNS?h.createElementNS(o.replace(/^https/,"http"),r):h.createElement(r);return t&&(n?(x=x||w(e),s.setAttribute("width",.01),s.setAttribute("height",.01),s.setAttribute("transform","translate("+i+","+a+")"),x.appendChild(s)):(g||((g=w(e)).style.cssText=l),s.style.cssText=l+"width:0.1px;height:0.1px;top:"+a+"px;left:"+i+"px",g.appendChild(s))),s}throw"Need document and parent."}function A(e,t,n,o,r,i,a){return e.a=t,e.b=n,e.c=o,e.d=r,e.e=i,e.f=a,e}var h,f,i,a,g,x,m,v,y,t,b="transform",T=b+"Origin",M=function _setDoc(e){var t=e.ownerDocument||e;!(b in e.style)&&"msTransform"in e.style&&(T=(b="msTransform")+"Origin");for(;t.parentNode&&(t=t.parentNode););if(f=window,m=new ge,t){i=(h=t).documentElement,a=t.body,(v=h.createElementNS("http://www.w3.org/2000/svg","g")).style.transform="none";var n=t.createElement("div"),o=t.createElement("div"),r=t&&(t.body||t.firstElementChild);r&&r.appendChild&&(r.appendChild(n),n.appendChild(o),n.setAttribute("style","position:static;transform:translate3d(0,0,1px)"),y=o.offsetParent!==n,r.removeChild(n))}return t},E=function _forceNonZeroScale(e){for(var t,n;e&&e!==a;)(n=e._gsap)&&n.uncache&&n.get(e,"x"),n&&!n.scaleX&&!n.scaleY&&n.renderTransform&&(n.scaleX=n.scaleY=1e-4,n.renderTransform(1,n),t?t.push(n):t=[n]),e=e.parentNode;return t},D=[],S=[],L=function _getDocScrollTop(){return f.pageYOffset||h.scrollTop||i.scrollTop||a.scrollTop||0},C=function _getDocScrollLeft(){return f.pageXOffset||h.scrollLeft||i.scrollLeft||a.scrollLeft||0},P=function _svgOwner(e){return e.ownerSVGElement||("svg"===(e.tagName+"").toLowerCase()?e:null)},N=function _isFixed(e){return"fixed"===f.getComputedStyle(e).position||((e=e.parentNode)&&1===e.nodeType?_isFixed(e):void 0)},k=function _placeSiblings(e,t){var n,o,r,i,a,l,s=P(e),c=e===s,d=s?D:S,p=e.parentNode,u=p&&!s&&p.shadowRoot&&p.shadowRoot.appendChild?p.shadowRoot:p;if(e===f)return e;if(d.length||d.push(w(e,1),w(e,2),w(e,3)),n=s?x:g,s)c?(i=-(r=function _getCTM(e){var t,n=e.getCTM();return n||(t=e.style[b],e.style[b]="none",e.appendChild(v),n=v.getCTM(),e.removeChild(v),t?e.style[b]=t:e.style.removeProperty(b.replace(/([A-Z])/g,"-$1").toLowerCase())),n||m.clone()}(e)).e/r.a,a=-r.f/r.d,o=m):e.getBBox?(r=e.getBBox(),i=(o=(o=e.transform?e.transform.baseVal:{}).numberOfItems?1o.scrollHeight,a.removeChild(o),R=n,Ee=function(e){for(var t=e.split(","),n=(("onpointerdown"in s?"pointerdown,pointermove,pointerup,pointercancel":"onmspointerdown"in s?"MSPointerDown,MSPointerMove,MSPointerUp,MSPointerCancel":e).split(",")),o={},r=4;-1<--r;)o[t[r]]=n[r],o[n[r]]=t[r];try{ye.addEventListener("test",null,Object.defineProperty({},"passive",{get:function get(){d=1}}))}catch(e){}return o}("touchstart,touchmove,touchend,touchcancel"),za(ve,"touchcancel",aa),za(me,"touchmove",aa),l&&l.addEventListener("touchstart",aa),za(ve,"contextmenu",function(){for(var e in Ie)Ie[e].isPressed&&Ie[e].endDrag()}),xe=be=Y()}var n,o,r,i,a;xe?(_e=xe.plugins.inertia,Le=xe.core.context||function(){},c=xe.utils.checkPrefix,O=c(O),Ye=c(Ye),Te=xe.utils.toArray,Ce=xe.core.getStyleSaver,B=!!c("perspective")):e&&console.warn("Please gsap.registerPlugin(Draggable)")}var xe,me,ve,ye,l,s,we,be,c,Te,d,Me,Ee,De,Se,_e,Xe,Ae,Le,Ce,B,R,n,Pe=0,O="transform",Ye="transformOrigin",Ne=Array.isArray,ke=180/Math.PI,Be=1e20,r=new ge,Re=Date.now||function(){return(new Date).getTime()},Oe=[],Ie={},Fe=0,o=/^(?:a|input|textarea|button|select)$/i,ze=0,He={},We={},Qe=function _isRoot(e){return!(e&&e!==ye&&9!==e.nodeType&&e!==ve.body&&e!==me&&e.nodeType&&e.parentNode)},p={},Ge={},u=function _getElementBounds(e,t){t=Te(t)[0];var n,o,r,i,a,l,s,c,d,p,u,h,f,g=e.getBBox&&e.ownerSVGElement,x=e.ownerDocument||ve;if(e===me)r=Fa(x),o=(n=Ga(x))+(x.documentElement.clientWidth||e.innerWidth||x.body.clientWidth||0),i=r+((e.innerHeight||0)-20d||l.rightp||l.bottom=l.width*l.height*n:o.width>n&&o.height>n))},Draggable);function Draggable(h,p){var e;e=Ve.call(this)||this,be||_a(1),h=Te(h)[0],e.styles=Ce&&Ce(h,"transform,left,top"),_e=_e||xe.plugins.inertia,e.vars=p=ra(p||{}),e.target=h,e.x=e.y=e.rotation=0,e.dragResistance=parseFloat(p.dragResistance)||0,e.edgeResistance=isNaN(p.edgeResistance)?1:parseFloat(p.edgeResistance)||0,e.lockAxis=p.lockAxis,e.autoScroll=p.autoScroll||0,e.lockedAxis=null,e.allowEventDefault=!!p.allowEventDefault,xe.getProperty(h,"x");function Sg(e,t){return parseFloat(se.get(h,e,t))}function zh(e){return Ba(e),e.stopImmediatePropagation&&e.stopImmediatePropagation(),!1}function Ah(e){if(J.autoScroll&&J.isDragging&&(te||C)){var t,n,o,r,i,a,l,s,c=h,d=15*J.autoScroll;for(te=!1,We.scrollTop=null!=me.pageYOffset?me.pageYOffset:null!=de.documentElement.scrollTop?de.documentElement.scrollTop:de.body.scrollTop,We.scrollLeft=null!=me.pageXOffset?me.pageXOffset:null!=de.documentElement.scrollLeft?de.documentElement.scrollLeft:de.body.scrollLeft,r=J.pointerX-We.scrollLeft,i=J.pointerY-We.scrollTop;c&&!n;)t=(n=Qe(c.parentNode))?We:c.parentNode,o=n?{bottom:Math.max(ye.clientHeight,me.innerHeight||0),right:Math.max(ye.clientWidth,me.innerWidth||0),left:0,top:0}:t.getBoundingClientRect(),a=l=0,U&&((s=t._gsMaxScrollY-t.scrollTop)<0?l=s:i>o.bottom-re&&s?(te=!0,l=Math.min(s,d*(1-Math.max(0,o.bottom-i)/re)|0)):io.right-oe&&s?(te=!0,a=Math.min(s,d*(1-Math.max(0,o.right-r)/oe)|0)):rj?(J.y=a,T+(M-a)*c):T):(k&&(s=e*k.a+t*k.c+k.e,t=e*k.b+t*k.d+k.f,e=s),(r=t-b)Math.abs(r)?"y":U?"x":null,s&&Z(J.vars.onLockAxis)&&J.vars.onLockAxis.call(J,J.pointerEvent)),"y"===s?r=0:"x"===s&&(o=0)),i=da(T+o*c),a=da(M+r*c)),(P||Y||N)&&(J.x!==i||J.y!==a&&!Q)&&(N&&(He.x=i,He.y=a,s=N(He),i=da(s.x),a=da(s.y)),P&&(i=da(P(i))),Y&&(a=da(Y(a)))),E&&(Da._gsMaxScrollY&&(a.scrollTop=a._gsMaxScrollY),K&&a.scrollLeft>a._gsMaxScrollX&&(a.scrollLeft=a._gsMaxScrollX),i=a;J.isThrowing&&(r||J.endX>D||J.endXX||J.endY {\n\t\tlet doc = element.ownerDocument || element;\n\t\tif (!(_transformProp in element.style) && \"msTransform\" in element.style) { //to improve compatibility with old Microsoft browsers\n\t\t\t_transformProp = \"msTransform\";\n\t\t\t_transformOriginProp = _transformProp + \"Origin\";\n\t\t}\n\t\twhile (doc.parentNode && (doc = doc.parentNode)) {\t}\n\t\t_win = window;\n\t\t_identityMatrix = new Matrix2D();\n\t\tif (doc) {\n\t\t\t_doc = doc;\n\t\t\t_docElement = doc.documentElement;\n\t\t\t_body = doc.body;\n\t\t\t_gEl = _doc.createElementNS(\"http://www.w3.org/2000/svg\", \"g\");\n\t\t\t// prevent any existing CSS from transforming it\n\t\t\t_gEl.style.transform = \"none\";\n\t\t\t// now test for the offset reporting bug. Use feature detection instead of browser sniffing to make things more bulletproof and future-proof. Hopefully Safari will fix their bug soon.\n\t\t\tlet d1 = doc.createElement(\"div\"),\n\t\t\t\td2 = doc.createElement(\"div\"),\n\t\t\t\troot = doc && (doc.body || doc.firstElementChild);\n\t\t\tif (root && root.appendChild) {\n\t\t\t\troot.appendChild(d1);\n\t\t\t\td1.appendChild(d2);\n\t\t\t\td1.setAttribute(\"style\", \"position:static;transform:translate3d(0,0,1px)\");\n\t\t\t\t_hasOffsetBug = (d2.offsetParent !== d1);\n\t\t\t\troot.removeChild(d1);\n\t\t\t}\n\t\t}\n\t\treturn doc;\n\t},\n\t_forceNonZeroScale = e => { // walks up the element's ancestors and finds any that had their scale set to 0 via GSAP, and changes them to 0.0001 to ensure that measurements work. Firefox has a bug that causes it to incorrectly report getBoundingClientRect() when scale is 0.\n\t\tlet a, cache;\n\t\twhile (e && e !== _body) {\n\t\t\tcache = e._gsap;\n\t\t\tcache && cache.uncache && cache.get(e, \"x\"); // force re-parsing of transforms if necessary\n\t\t\tif (cache && !cache.scaleX && !cache.scaleY && cache.renderTransform) {\n\t\t\t\tcache.scaleX = cache.scaleY = 1e-4;\n\t\t\t\tcache.renderTransform(1, cache);\n\t\t\t\ta ? a.push(cache) : (a = [cache]);\n\t\t\t}\n\t\t\te = e.parentNode;\n\t\t}\n\t\treturn a;\n\t},\n\t// possible future addition: pass an element to _forceDisplay() and it'll walk up all its ancestors and make sure anything with display: none is set to display: block, and if there's no parentNode, it'll add it to the body. It returns an Array that you can then feed to _revertDisplay() to have it revert all the changes it made.\n\t// _forceDisplay = e => {\n\t// \tlet a = [],\n\t// \t\tparent;\n\t// \twhile (e && e !== _body) {\n\t// \t\tparent = e.parentNode;\n\t// \t\t(_win.getComputedStyle(e).display === \"none\" || !parent) && a.push(e, e.style.display, parent) && (e.style.display = \"block\");\n\t// \t\tparent || _body.appendChild(e);\n\t// \t\te = parent;\n\t// \t}\n\t// \treturn a;\n\t// },\n\t// _revertDisplay = a => {\n\t// \tfor (let i = 0; i < a.length; i+=3) {\n\t// \t\ta[i+1] ? (a[i].style.display = a[i+1]) : a[i].style.removeProperty(\"display\");\n\t// \t\ta[i+2] || a[i].parentNode.removeChild(a[i]);\n\t// \t}\n\t// },\n\t_svgTemps = [], //we create 3 elements for SVG, and 3 for other DOM elements and cache them for performance reasons. They get nested in _divContainer and _svgContainer so that just one element is added to the DOM on each successive attempt. Again, performance is key.\n\t_divTemps = [],\n\t_getDocScrollTop = () => _win.pageYOffset || _doc.scrollTop || _docElement.scrollTop || _body.scrollTop || 0,\n\t_getDocScrollLeft = () => _win.pageXOffset || _doc.scrollLeft || _docElement.scrollLeft || _body.scrollLeft || 0,\n\t_svgOwner = element => element.ownerSVGElement || ((element.tagName + \"\").toLowerCase() === \"svg\" ? element : null),\n\t_isFixed = element => {\n\t\tif (_win.getComputedStyle(element).position === \"fixed\") {\n\t\t\treturn true;\n\t\t}\n\t\telement = element.parentNode;\n\t\tif (element && element.nodeType === 1) { // avoid document fragments which will throw an error.\n\t\t\treturn _isFixed(element);\n\t\t}\n\t},\n\t_createSibling = (element, i) => {\n\t\tif (element.parentNode && (_doc || _setDoc(element))) {\n\t\t\tlet svg = _svgOwner(element),\n\t\t\t\tns = svg ? (svg.getAttribute(\"xmlns\") || \"http://www.w3.org/2000/svg\") : \"http://www.w3.org/1999/xhtml\",\n\t\t\t\ttype = svg ? (i ? \"rect\" : \"g\") : \"div\",\n\t\t\t\tx = i !== 2 ? 0 : 100,\n\t\t\t\ty = i === 3 ? 100 : 0,\n\t\t\t\tcss = \"position:absolute;display:block;pointer-events:none;margin:0;padding:0;\",\n\t\t\t\te = _doc.createElementNS ? _doc.createElementNS(ns.replace(/^https/, \"http\"), type) : _doc.createElement(type);\n\t\t\tif (i) {\n\t\t\t\tif (!svg) {\n\t\t\t\t\tif (!_divContainer) {\n\t\t\t\t\t\t_divContainer = _createSibling(element);\n\t\t\t\t\t\t_divContainer.style.cssText = css;\n\t\t\t\t\t}\n\t\t\t\t\te.style.cssText = css + \"width:0.1px;height:0.1px;top:\" + y + \"px;left:\" + x + \"px\";\n\t\t\t\t\t_divContainer.appendChild(e);\n\n\t\t\t\t} else {\n\t\t\t\t\t_svgContainer || (_svgContainer = _createSibling(element));\n\t\t\t\t\te.setAttribute(\"width\", 0.01);\n\t\t\t\t\te.setAttribute(\"height\", 0.01);\n\t\t\t\t\te.setAttribute(\"transform\", \"translate(\" + x + \",\" + y + \")\");\n\t\t\t\t\t_svgContainer.appendChild(e);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn e;\n\t\t}\n\t\tthrow \"Need document and parent.\";\n\t},\n\t_consolidate = m => { // replaces SVGTransformList.consolidate() because a bug in Firefox causes it to break pointer events. See https://gsap.com/forums/topic/23248-touch-is-not-working-on-draggable-in-firefox-windows-v324/?tab=comments#comment-109800\n\t\tlet c = new Matrix2D(),\n\t\t\ti = 0;\n\t\tfor (; i < m.numberOfItems; i++) {\n\t\t\tc.multiply(m.getItem(i).matrix);\n\t\t}\n\t\treturn c;\n\t},\n\t_getCTM = svg => {\n\t\tlet m = svg.getCTM(),\n\t\t\ttransform;\n\t\tif (!m) { // Firefox returns null for getCTM() on root elements, so this is a workaround using a that we temporarily append.\n\t\t\ttransform = svg.style[_transformProp];\n\t\t\tsvg.style[_transformProp] = \"none\"; // a bug in Firefox causes css transforms to contaminate the getCTM()\n\t\t\tsvg.appendChild(_gEl);\n\t\t\tm = _gEl.getCTM();\n\t\t\tsvg.removeChild(_gEl);\n\t\t\ttransform ? (svg.style[_transformProp] = transform) : svg.style.removeProperty(_transformProp.replace(/([A-Z])/g, \"-$1\").toLowerCase());\n\t\t}\n\t\treturn m || _identityMatrix.clone(); // Firefox will still return null if the has a width/height of 0 in the browser.\n\t},\n\t_placeSiblings = (element, adjustGOffset) => {\n\t\tlet svg = _svgOwner(element),\n\t\t\tisRootSVG = element === svg,\n\t\t\tsiblings = svg ? _svgTemps : _divTemps,\n\t\t\tparent = element.parentNode,\n\t\t\tappendToEl = parent && !svg && parent.shadowRoot && parent.shadowRoot.appendChild ? parent.shadowRoot : parent,\n\t\t\tcontainer, m, b, x, y, cs;\n\t\tif (element === _win) {\n\t\t\treturn element;\n\t\t}\n\t\tsiblings.length || siblings.push(_createSibling(element, 1), _createSibling(element, 2), _createSibling(element, 3));\n\t\tcontainer = svg ? _svgContainer : _divContainer;\n\t\tif (svg) {\n\t\t\tif (isRootSVG) {\n\t\t\t\tb = _getCTM(element);\n\t\t\t\tx = -b.e / b.a;\n\t\t\t\ty = -b.f / b.d;\n\t\t\t\tm = _identityMatrix;\n\t\t\t} else if (element.getBBox) {\n\t\t\t\tb = element.getBBox();\n\t\t\t\tm = element.transform ? element.transform.baseVal : {}; // IE11 doesn't follow the spec.\n\t\t\t\tm = !m.numberOfItems ? _identityMatrix : m.numberOfItems > 1 ? _consolidate(m) : m.getItem(0).matrix; // don't call m.consolidate().matrix because a bug in Firefox makes pointer events not work when consolidate() is called on the same tick as getBoundingClientRect()! See https://gsap.com/forums/topic/23248-touch-is-not-working-on-draggable-in-firefox-windows-v324/?tab=comments#comment-109800\n\t\t\t\tx = m.a * b.x + m.c * b.y;\n\t\t\t\ty = m.b * b.x + m.d * b.y;\n\t\t\t} else { // may be a which has no getBBox() so just use defaults instead of throwing errors.\n\t\t\t\tm = new Matrix2D();\n\t\t\t\tx = y = 0;\n\t\t\t}\n\t\t\tif (adjustGOffset && element.tagName.toLowerCase() === \"g\") {\n\t\t\t\tx = y = 0;\n\t\t\t}\n\t\t\t(isRootSVG ? svg : parent).appendChild(container);\n\t\t\tcontainer.setAttribute(\"transform\", \"matrix(\" + m.a + \",\" + m.b + \",\" + m.c + \",\" + m.d + \",\" + (m.e + x) + \",\" + (m.f + y) + \")\");\n\t\t} else {\n\t\t\tx = y = 0;\n\t\t\tif (_hasOffsetBug) { // some browsers (like Safari) have a bug that causes them to misreport offset values. When an ancestor element has a transform applied, it's supposed to treat it as if it's position: relative (new context). Safari botches this, so we need to find the closest ancestor (between the element and its offsetParent) that has a transform applied and if one is found, grab its offsetTop/Left and subtract them to compensate.\n\t\t\t\tm = element.offsetParent;\n\t\t\t\tb = element;\n\t\t\t\twhile (b && (b = b.parentNode) && b !== m && b.parentNode) {\n\t\t\t\t\tif ((_win.getComputedStyle(b)[_transformProp] + \"\").length > 4) {\n\t\t\t\t\t\tx = b.offsetLeft;\n\t\t\t\t\t\ty = b.offsetTop;\n\t\t\t\t\t\tb = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tcs = _win.getComputedStyle(element);\n\t\t\tif (cs.position !== \"absolute\" && cs.position !== \"fixed\") {\n\t\t\t\tm = element.offsetParent;\n\t\t\t\twhile (parent && parent !== m) { // if there's an ancestor element between the element and its offsetParent that's scrolled, we must factor that in.\n\t\t\t\t\tx += parent.scrollLeft || 0;\n\t\t\t\t\ty += parent.scrollTop || 0;\n\t\t\t\t\tparent = parent.parentNode;\n\t\t\t\t}\n\t\t\t}\n\t\t\tb = container.style;\n\t\t\tb.top = (element.offsetTop - y) + \"px\";\n\t\t\tb.left = (element.offsetLeft - x) + \"px\";\n\t\t\tb[_transformProp] = cs[_transformProp];\n\t\t\tb[_transformOriginProp] = cs[_transformOriginProp];\n\t\t\t// b.border = m.border;\n\t\t\t// b.borderLeftStyle = m.borderLeftStyle;\n\t\t\t// b.borderTopStyle = m.borderTopStyle;\n\t\t\t// b.borderLeftWidth = m.borderLeftWidth;\n\t\t\t// b.borderTopWidth = m.borderTopWidth;\n\t\t\tb.position = cs.position === \"fixed\" ? \"fixed\" : \"absolute\";\n\t\t\tappendToEl.appendChild(container);\n\t\t}\n\t\treturn container;\n\t},\n\t_setMatrix = (m, a, b, c, d, e, f) => {\n\t\tm.a = a;\n\t\tm.b = b;\n\t\tm.c = c;\n\t\tm.d = d;\n\t\tm.e = e;\n\t\tm.f = f;\n\t\treturn m;\n\t};\n\nexport class Matrix2D {\n\tconstructor(a=1, b=0, c=0, d=1, e=0, f=0) {\n\t\t_setMatrix(this, a, b, c, d, e, f);\n\t}\n\n\tinverse() {\n\t\tlet {a, b, c, d, e, f} = this,\n\t\t\tdeterminant = (a * d - b * c) || 1e-10;\n\t\treturn _setMatrix(\n\t\t\tthis,\n\t\t\td / determinant,\n\t\t\t-b / determinant,\n\t\t\t-c / determinant,\n\t\t\ta / determinant,\n\t\t\t(c * f - d * e) / determinant,\n\t\t\t-(a * f - b * e) / determinant\n\t\t);\n\t}\n\n\tmultiply(matrix) {\n\t\tlet {a, b, c, d, e, f} = this,\n\t\t\ta2 = matrix.a,\n\t\t\tb2 = matrix.c,\n\t\t\tc2 = matrix.b,\n\t\t\td2 = matrix.d,\n\t\t\te2 = matrix.e,\n\t\t\tf2 = matrix.f;\n\t\treturn _setMatrix(this,\n\t\t\ta2 * a + c2 * c,\n\t\t\ta2 * b + c2 * d,\n\t\t\tb2 * a + d2 * c,\n\t\t\tb2 * b + d2 * d,\n\t\t\te + e2 * a + f2 * c,\n\t\t\tf + e2 * b + f2 * d);\n\t}\n\n\tclone() {\n\t\treturn new Matrix2D(this.a, this.b, this.c, this.d, this.e, this.f);\n\t}\n\n\tequals(matrix) {\n\t\tlet {a, b, c, d, e, f} = this;\n\t\treturn (a === matrix.a && b === matrix.b && c === matrix.c && d === matrix.d && e === matrix.e && f === matrix.f);\n\t}\n\n\tapply(point, decoratee={}) {\n\t\tlet {x, y} = point,\n\t\t\t{a, b, c, d, e, f} = this;\n\t\tdecoratee.x = (x * a + y * c + e) || 0;\n\t\tdecoratee.y = (x * b + y * d + f) || 0;\n\t\treturn decoratee;\n\t}\n\n}\n\n// Feed in an element and it'll return a 2D matrix (optionally inverted) so that you can translate between coordinate spaces.\n// Inverting lets you translate a global point into a local coordinate space. No inverting lets you go the other way.\n// We needed this to work around various browser bugs, like Firefox doesn't accurately report getScreenCTM() when there\n// are transforms applied to ancestor elements.\n// The matrix math to convert any x/y coordinate is as follows, which is wrapped in a convenient apply() method of Matrix2D above:\n// tx = m.a * x + m.c * y + m.e\n// ty = m.b * x + m.d * y + m.f\nexport function getGlobalMatrix(element, inverse, adjustGOffset, includeScrollInFixed) { // adjustGOffset is typically used only when grabbing an element's PARENT's global matrix, and it ignores the x/y offset of any SVG elements because they behave in a special way.\n\tif (!element || !element.parentNode || (_doc || _setDoc(element)).documentElement === element) {\n\t\treturn new Matrix2D();\n\t}\n\tlet zeroScales = _forceNonZeroScale(element),\n\t\tsvg = _svgOwner(element),\n\t\ttemps = svg ? _svgTemps : _divTemps,\n\t\tcontainer = _placeSiblings(element, adjustGOffset),\n\t\tb1 = temps[0].getBoundingClientRect(),\n\t\tb2 = temps[1].getBoundingClientRect(),\n\t\tb3 = temps[2].getBoundingClientRect(),\n\t\tparent = container.parentNode,\n\t\tisFixed = !includeScrollInFixed && _isFixed(element),\n\t\tm = new Matrix2D(\n\t\t\t(b2.left - b1.left) / 100,\n\t\t\t(b2.top - b1.top) / 100,\n\t\t\t(b3.left - b1.left) / 100,\n\t\t\t(b3.top - b1.top) / 100,\n\t\t\tb1.left + (isFixed ? 0 : _getDocScrollLeft()),\n\t\t\tb1.top + (isFixed ? 0 : _getDocScrollTop())\n\t\t);\n\tparent.removeChild(container);\n\tif (zeroScales) {\n\t\tb1 = zeroScales.length;\n\t\twhile (b1--) {\n\t\t\tb2 = zeroScales[b1];\n\t\t\tb2.scaleX = b2.scaleY = 0;\n\t\t\tb2.renderTransform(1, b2);\n\t\t}\n\t}\n\treturn inverse ? m.inverse() : m;\n}\n\nexport { _getDocScrollTop, _getDocScrollLeft, _setDoc, _isFixed, _getCTM };\n\n// export function getMatrix(element) {\n// \t_doc || _setDoc(element);\n// \tlet m = (_win.getComputedStyle(element)[_transformProp] + \"\").substr(7).match(/[-.]*\\d+[.e\\-+]*\\d*[e\\-\\+]*\\d*/g),\n// \t\tis2D = m && m.length === 6;\n// \treturn !m || m.length < 6 ? new Matrix2D() : new Matrix2D(+m[0], +m[1], +m[is2D ? 2 : 4], +m[is2D ? 3 : 5], +m[is2D ? 4 : 12], +m[is2D ? 5 : 13]);\n// }","/*!\n * Draggable 3.13.0\n * https://gsap.com\n *\n * @license Copyright 2008-2025, GreenSock. All rights reserved.\n * Subject to the terms at https://gsap.com/standard-license\n * @author: Jack Doyle, jack@greensock.com\n */\n/* eslint-disable */\n\nimport { getGlobalMatrix, Matrix2D } from \"./utils/matrix.js\";\n\nlet gsap, _win, _doc, _docElement, _body, _tempDiv, _placeholderDiv, _coreInitted, _checkPrefix, _toArray, _supportsPassive, _isTouchDevice, _touchEventLookup, _isMultiTouching, _isAndroid, InertiaPlugin, _defaultCursor, _supportsPointer, _context, _getStyleSaver,\n\t_dragCount = 0,\n\t_windowExists = () => typeof(window) !== \"undefined\",\n\t_getGSAP = () => gsap || (_windowExists() && (gsap = window.gsap) && gsap.registerPlugin && gsap),\n\t_isFunction = value => typeof(value) === \"function\",\n\t_isObject = value => typeof(value) === \"object\",\n\t_isUndefined = value => typeof(value) === \"undefined\",\n\t_emptyFunc = () => false,\n\t_transformProp = \"transform\",\n\t_transformOriginProp = \"transformOrigin\",\n\t_round = value => Math.round(value * 10000) / 10000,\n\t_isArray = Array.isArray,\n\t_createElement = (type, ns) => {\n\t\tlet e = _doc.createElementNS ? _doc.createElementNS((ns || \"http://www.w3.org/1999/xhtml\").replace(/^https/, \"http\"), type) : _doc.createElement(type); //some servers swap in https for http in the namespace which can break things, making \"style\" inaccessible.\n\t\treturn e.style ? e : _doc.createElement(type); //some environments won't allow access to the element's style when created with a namespace in which case we default to the standard createElement() to work around the issue. Also note that when GSAP is embedded directly inside an SVG file, createElement() won't allow access to the style object in Firefox (see https://gsap.com/forums/topic/20215-problem-using-tweenmax-in-standalone-self-containing-svg-file-err-cannot-set-property-csstext-of-undefined/).\n\t},\n\t_RAD2DEG = 180 / Math.PI,\n\t_bigNum = 1e20,\n\t_identityMatrix = new Matrix2D(),\n\t_getTime = Date.now || (() => new Date().getTime()),\n\t_renderQueue = [],\n\t_lookup = {}, //when a Draggable is created, the target gets a unique _gsDragID property that allows gets associated with the Draggable instance for quick lookups in Draggable.get(). This avoids circular references that could cause gc problems.\n\t_lookupCount = 0,\n\t_clickableTagExp = /^(?:a|input|textarea|button|select)$/i,\n\t_lastDragTime = 0,\n\t_temp1 = {}, // a simple object we reuse and populate (usually x/y properties) to conserve memory and improve performance.\n\t_windowProxy = {}, //memory/performance optimization - we reuse this object during autoScroll to store window-related bounds/offsets.\n\t_copy = (obj, factor) => {\n\t\tlet copy = {}, p;\n\t\tfor (p in obj) {\n\t\t\tcopy[p] = factor ? obj[p] * factor : obj[p];\n\t\t}\n\t\treturn copy;\n\t},\n\t_extend = (obj, defaults) => {\n\t\tfor (let p in defaults) {\n\t\t\tif (!(p in obj)) {\n\t\t\t\tobj[p] = defaults[p];\n\t\t\t}\n\t\t}\n\t\treturn obj;\n\t},\n\t_setTouchActionForAllDescendants = (elements, value) => {\n\t\tlet i = elements.length,\n\t\t\tchildren;\n\t\twhile (i--) {\n\t\t\tvalue ? (elements[i].style.touchAction = value) : elements[i].style.removeProperty(\"touch-action\");\n\t\t\tchildren = elements[i].children;\n\t\t\tchildren && children.length && _setTouchActionForAllDescendants(children, value);\n\t\t}\n\t},\n\t_renderQueueTick = () => _renderQueue.forEach(func => func()),\n\t_addToRenderQueue = func => {\n\t\t_renderQueue.push(func);\n\t\tif (_renderQueue.length === 1) {\n\t\t\tgsap.ticker.add(_renderQueueTick);\n\t\t}\n\t},\n\t_renderQueueTimeout = () => !_renderQueue.length && gsap.ticker.remove(_renderQueueTick),\n\t_removeFromRenderQueue = func => {\n\t\tlet i = _renderQueue.length;\n\t\twhile (i--) {\n\t\t\tif (_renderQueue[i] === func) {\n\t\t\t\t_renderQueue.splice(i, 1);\n\t\t\t}\n\t\t}\n\t\tgsap.to(_renderQueueTimeout, {overwrite:true, delay:15, duration:0, onComplete:_renderQueueTimeout, data:\"_draggable\"}); //remove the \"tick\" listener only after the render queue is empty for 15 seconds (to improve performance). Adding/removing it constantly for every click/touch wouldn't deliver optimal speed, and we also don't want the ticker to keep calling the render method when things are idle for long periods of time (we want to improve battery life on mobile devices).\n\t},\n\t_setDefaults = (obj, defaults) => {\n\t\tfor (let p in defaults) {\n\t\t\tif (!(p in obj)) {\n\t\t\t\tobj[p] = defaults[p];\n\t\t\t}\n\t\t}\n\t\treturn obj;\n\t},\n\t_addListener = (element, type, func, capture) => {\n\t\tif (element.addEventListener) {\n\t\t\tlet touchType = _touchEventLookup[type];\n\t\t\tcapture = capture || (_supportsPassive ? {passive: false} : null);\n\t\t\telement.addEventListener(touchType || type, func, capture);\n\t\t\t(touchType && type !== touchType) && element.addEventListener(type, func, capture);//some browsers actually support both, so must we. But pointer events cover all.\n\t\t}\n\t},\n\t_removeListener = (element, type, func, capture) => {\n\t\tif (element.removeEventListener) {\n\t\t\tlet touchType = _touchEventLookup[type];\n\t\t\telement.removeEventListener(touchType || type, func, capture);\n\t\t\t(touchType && type !== touchType) && element.removeEventListener(type, func, capture);\n\t\t}\n\t},\n\t_preventDefault = event => {\n\t\tevent.preventDefault && event.preventDefault();\n\t\tevent.preventManipulation && event.preventManipulation(); //for some Microsoft browsers\n\t},\n\t_hasTouchID = (list, ID) => {\n\t\tlet i = list.length;\n\t\twhile (i--) {\n\t\t\tif (list[i].identifier === ID) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t},\n\t_onMultiTouchDocumentEnd = event => {\n\t\t_isMultiTouching = (event.touches && _dragCount < event.touches.length);\n\t\t_removeListener(event.target, \"touchend\", _onMultiTouchDocumentEnd);\n\t},\n\n\t_onMultiTouchDocument = event => {\n\t\t_isMultiTouching = (event.touches && _dragCount < event.touches.length);\n\t\t_addListener(event.target, \"touchend\", _onMultiTouchDocumentEnd);\n\t},\n\t_getDocScrollTop = doc => _win.pageYOffset || doc.scrollTop || doc.documentElement.scrollTop || doc.body.scrollTop || 0,\n\t_getDocScrollLeft = doc => _win.pageXOffset || doc.scrollLeft || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0,\n\t_addScrollListener = (e, callback) => {\n\t\t_addListener(e, \"scroll\", callback);\n\t\tif (!_isRoot(e.parentNode)) {\n\t\t\t_addScrollListener(e.parentNode, callback);\n\t\t}\n\t},\n\t_removeScrollListener = (e, callback) => {\n\t\t_removeListener(e, \"scroll\", callback);\n\t\tif (!_isRoot(e.parentNode)) {\n\t\t\t_removeScrollListener(e.parentNode, callback);\n\t\t}\n\t},\n\t_isRoot = e => !!(!e || e === _docElement || e.nodeType === 9 || e === _doc.body || e === _win || !e.nodeType || !e.parentNode),\n\t_getMaxScroll = (element, axis) => {\n\t\tlet dim = (axis === \"x\") ? \"Width\" : \"Height\",\n\t\t\tscroll = \"scroll\" + dim,\n\t\t\tclient = \"client\" + dim;\n\t\treturn Math.max(0, _isRoot(element) ? Math.max(_docElement[scroll], _body[scroll]) - (_win[\"inner\" + dim] || _docElement[client] || _body[client]) : element[scroll] - element[client]);\n\t},\n\t_recordMaxScrolls = (e, skipCurrent) => { //records _gsMaxScrollX and _gsMaxScrollY properties for the element and all ancestors up the chain so that we can cap it, otherwise dragging beyond the edges with autoScroll on can endlessly scroll.\n\t\tlet x = _getMaxScroll(e, \"x\"),\n\t\t\ty = _getMaxScroll(e, \"y\");\n\t\tif (_isRoot(e)) {\n\t\t\te = _windowProxy;\n\t\t} else {\n\t\t\t_recordMaxScrolls(e.parentNode, skipCurrent);\n\t\t}\n\t\te._gsMaxScrollX = x;\n\t\te._gsMaxScrollY = y;\n\t\tif (!skipCurrent) {\n\t\t\te._gsScrollX = e.scrollLeft || 0;\n\t\t\te._gsScrollY = e.scrollTop || 0;\n\t\t}\n\t},\n\t_setStyle = (element, property, value) => {\n\t\tlet style = element.style;\n\t\tif (!style) {\n\t\t\treturn;\n\t\t}\n\t\tif (_isUndefined(style[property])) {\n\t\t\tproperty = _checkPrefix(property, element) || property;\n\t\t}\n\t\tif (value == null) {\n\t\t\tstyle.removeProperty && style.removeProperty(property.replace(/([A-Z])/g, \"-$1\").toLowerCase());\n\t\t} else {\n\t\t\tstyle[property] = value;\n\t\t}\n\t},\n\t_getComputedStyle = element => _win.getComputedStyle((element instanceof Element) ? element : element.host || (element.parentNode || {}).host || element), //the \"host\" stuff helps to accommodate ShadowDom objects.\n\n\t_tempRect = {}, //reuse to reduce garbage collection tasks\n\t_parseRect = e => { //accepts a DOM element, a mouse event, or a rectangle object and returns the corresponding rectangle with left, right, width, height, top, and bottom properties\n\t\tif (e === _win) {\n\t\t\t_tempRect.left = _tempRect.top = 0;\n\t\t\t_tempRect.width = _tempRect.right = _docElement.clientWidth || e.innerWidth || _body.clientWidth || 0;\n\t\t\t_tempRect.height = _tempRect.bottom = ((e.innerHeight || 0) - 20 < _docElement.clientHeight) ? _docElement.clientHeight : e.innerHeight || _body.clientHeight || 0;\n\t\t\treturn _tempRect;\n\t\t}\n\t\tlet doc = e.ownerDocument || _doc,\n\t\t\tr = !_isUndefined(e.pageX) ? {left: e.pageX - _getDocScrollLeft(doc), top: e.pageY - _getDocScrollTop(doc), right: e.pageX - _getDocScrollLeft(doc) + 1, bottom: e.pageY - _getDocScrollTop(doc) + 1} : (!e.nodeType && !_isUndefined(e.left) && !_isUndefined(e.top)) ? e : _toArray(e)[0].getBoundingClientRect();\n\t\tif (_isUndefined(r.right) && !_isUndefined(r.width)) {\n\t\t\tr.right = r.left + r.width;\n\t\t\tr.bottom = r.top + r.height;\n\t\t} else if (_isUndefined(r.width)) { //some browsers don't include width and height properties. We can't just set them directly on r because some browsers throw errors, so create a new generic object.\n\t\t\tr = {width: r.right - r.left, height: r.bottom - r.top, right: r.right, left: r.left, bottom: r.bottom, top: r.top};\n\t\t}\n\t\treturn r;\n\t},\n\n\t_dispatchEvent = (target, type, callbackName) => {\n\t\tlet vars = target.vars,\n\t\t\tcallback = vars[callbackName],\n\t\t\tlisteners = target._listeners[type],\n\t\t\tresult;\n\t\tif (_isFunction(callback)) {\n\t\t\tresult = callback.apply(vars.callbackScope || target, vars[callbackName + \"Params\"] || [target.pointerEvent]);\n\t\t}\n\t\tif (listeners && target.dispatchEvent(type) === false) {\n\t\t\tresult = false;\n\t\t}\n\t\treturn result;\n\t},\n\t_getBounds = (target, context) => { //accepts any of the following: a DOM element, jQuery object, selector text, or an object defining bounds as {top, left, width, height} or {minX, maxX, minY, maxY}. Returns an object with left, top, width, and height properties.\n\t\tlet e = _toArray(target)[0],\n\t\t\ttop, left, offset;\n\t\tif (!e.nodeType && e !== _win) {\n\t\t\tif (!_isUndefined(target.left)) {\n\t\t\t\toffset = {x:0, y:0}; //_getOffsetTransformOrigin(context); //the bounds should be relative to the origin\n\t\t\t\treturn {left: target.left - offset.x, top: target.top - offset.y, width: target.width, height: target.height};\n\t\t\t}\n\t\t\tleft = target.min || target.minX || target.minRotation || 0;\n\t\t\ttop = target.min || target.minY || 0;\n\t\t\treturn {left:left, top:top, width:(target.max || target.maxX || target.maxRotation || 0) - left, height:(target.max || target.maxY || 0) - top};\n\t\t}\n\t\treturn _getElementBounds(e, context);\n\t},\n\t_point1 = {}, //we reuse to minimize garbage collection tasks.\n\t_getElementBounds = (element, context) => {\n\t\tcontext = _toArray(context)[0];\n\t\tlet isSVG = (element.getBBox && element.ownerSVGElement),\n\t\t\tdoc = element.ownerDocument || _doc,\n\t\t\tleft, right, top, bottom, matrix, p1, p2, p3, p4, bbox, width, height, cs;\n\t\tif (element === _win) {\n\t\t\ttop = _getDocScrollTop(doc);\n\t\t\tleft = _getDocScrollLeft(doc);\n\t\t\tright = left + (doc.documentElement.clientWidth || element.innerWidth || doc.body.clientWidth || 0);\n\t\t\tbottom = top + (((element.innerHeight || 0) - 20 < doc.documentElement.clientHeight) ? doc.documentElement.clientHeight : element.innerHeight || doc.body.clientHeight || 0); //some browsers (like Firefox) ignore absolutely positioned elements, and collapse the height of the documentElement, so it could be 8px, for example, if you have just an absolutely positioned div. In that case, we use the innerHeight to resolve this.\n\t\t} else if (context === _win || _isUndefined(context)) {\n\t\t\treturn element.getBoundingClientRect();\n\t\t} else {\n\t\t\tleft = top = 0;\n\t\t\tif (isSVG) {\n\t\t\t\tbbox = element.getBBox();\n\t\t\t\twidth = bbox.width;\n\t\t\t\theight = bbox.height;\n\t\t\t} else {\n\t\t\t\tif (element.viewBox && (bbox = element.viewBox.baseVal)) {\n\t\t\t\t\tleft = bbox.x || 0;\n\t\t\t\t\ttop = bbox.y || 0;\n\t\t\t\t\twidth = bbox.width;\n\t\t\t\t\theight = bbox.height;\n\t\t\t\t}\n\t\t\t\tif (!width) {\n\t\t\t\t\tcs = _getComputedStyle(element);\n\t\t\t\t\tbbox = cs.boxSizing === \"border-box\";\n\t\t\t\t\twidth = (parseFloat(cs.width) || element.clientWidth || 0) + (bbox ? 0 : parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth));\n\t\t\t\t\theight = (parseFloat(cs.height) || element.clientHeight || 0) + (bbox ? 0 : parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth));\n\t\t\t\t}\n\t\t\t}\n\t\t\tright = width;\n\t\t\tbottom = height;\n\t\t}\n\t\tif (element === context) {\n\t\t\treturn {left:left, top:top, width: right - left, height: bottom - top};\n\t\t}\n\t\tmatrix = getGlobalMatrix(context, true).multiply(getGlobalMatrix(element));\n\t\tp1 = matrix.apply({x:left, y:top});\n\t\tp2 = matrix.apply({x:right, y:top});\n\t\tp3 = matrix.apply({x:right, y:bottom});\n\t\tp4 = matrix.apply({x:left, y:bottom});\n\t\tleft = Math.min(p1.x, p2.x, p3.x, p4.x);\n\t\ttop = Math.min(p1.y, p2.y, p3.y, p4.y);\n\t\treturn {left: left, top: top, width: Math.max(p1.x, p2.x, p3.x, p4.x) - left, height: Math.max(p1.y, p2.y, p3.y, p4.y) - top};\n\t},\n\t_parseInertia = (draggable, snap, max, min, factor, forceZeroVelocity) => {\n\t\tlet vars = {},\n\t\t\ta, i, l;\n\t\tif (snap) {\n\t\t\tif (factor !== 1 && snap instanceof Array) { //some data must be altered to make sense, like if the user passes in an array of rotational values in degrees, we must convert it to radians. Or for scrollLeft and scrollTop, we invert the values.\n\t\t\t\tvars.end = a = [];\n\t\t\t\tl = snap.length;\n\t\t\t\tif (_isObject(snap[0])) { //if the array is populated with objects, like points ({x:100, y:200}), make copies before multiplying by the factor, otherwise we'll mess up the originals and the user may reuse it elsewhere.\n\t\t\t\t\tfor (i = 0; i < l; i++) {\n\t\t\t\t\t\ta[i] = _copy(snap[i], factor);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfor (i = 0; i < l; i++) {\n\t\t\t\t\t\ta[i] = snap[i] * factor;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmax += 1.1; //allow 1.1 pixels of wiggle room when snapping in order to work around some browser inconsistencies in the way bounds are reported which can make them roughly a pixel off. For example, if \"snap:[-$('#menu').width(), 0]\" was defined and #menu had a wrapper that was used as the bounds, some browsers would be one pixel off, making the minimum -752 for example when snap was [-753,0], thus instead of snapping to -753, it would snap to 0 since -753 was below the minimum.\n\t\t\t\tmin -= 1.1;\n\t\t\t} else if (_isFunction(snap)) {\n\t\t\t\tvars.end = value => {\n\t\t\t\t\tlet result = snap.call(draggable, value),\n\t\t\t\t\t\tcopy, p;\n\t\t\t\t\tif (factor !== 1) {\n\t\t\t\t\t\tif (_isObject(result)) {\n\t\t\t\t\t\t\tcopy = {};\n\t\t\t\t\t\t\tfor (p in result) {\n\t\t\t\t\t\t\t\tcopy[p] = result[p] * factor;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tresult = copy;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tresult *= factor;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn result; //we need to ensure that we can scope the function call to the Draggable instance itself so that users can access important values like maxX, minX, maxY, minY, x, and y from within that function.\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tvars.end = snap;\n\t\t\t}\n\t\t}\n\t\tif (max || max === 0) {\n\t\t\tvars.max = max;\n\t\t}\n\t\tif (min || min === 0) {\n\t\t\tvars.min = min;\n\t\t}\n\t\tif (forceZeroVelocity) {\n\t\t\tvars.velocity = 0;\n\t\t}\n\t\treturn vars;\n\t},\n\t_isClickable = element => { //sometimes it's convenient to mark an element as clickable by adding a data-clickable=\"true\" attribute (in which case we won't preventDefault() the mouse/touch event). This method checks if the element is an ,
0.00 / 0.00
';\n\t\tif (element) {\n\t\t\troot.style.position = \"absolute\";\n\t\t\troot.style.top = minimal ? \"calc(100% - 42px)\" : \"calc(100% - 51px)\";\n\t\t}\n\t\tif (css) {\n\t\t\tif (_isString(css)) {\n\t\t\t\troot.style.cssText = css;\n\t\t\t} else if (_isObject(css)) {\n\t\t\t\tcss.data = \"root\";\n\t\t\t\tgsap.set(root, css).kill();\n\t\t\t}\n\t\t\tif (root.style.top) {\n\t\t\t\troot.style.bottom = \"auto\";\n\t\t\t}\n\t\t\tif (root.style.width) {\n\t\t\t\tgsap.set(root, {xPercent: -50, left: \"50%\", right: \"auto\", data:\"root\"}).kill();\n\t\t\t}\n\t\t}\n\t\tif (!minimal && root.offsetWidth < 600) {\n\t\t\troot.setAttribute(\"class\", \"gs-dev-tools minimal\");\n\t\t\tif (element) {\n\t\t\t\troot.style.top = \"calc(100% - 42px)\";\n\t\t\t}\n\t\t}\n\t\treturn root;\n\t},\n\t_clickedOnce = true, //perhaps we shouldn't preventDefault() on the first mousedown/touchstart/pointerdown so that iframes get focus properly. Did that previously, but now it seems to prevent interaction on the first click (annoying).\n\t_addListener = (e, type, callback, capture) => {\n\t\tlet handler, altType;\n\t\tif (type === \"mousedown\" || type === \"mouseup\") {\n\t\t\te.style.cursor = \"pointer\";\n\t\t}\n\t\tif (type === \"mousedown\") {\n\t\t\t//some browsers call BOTH mousedown AND touchstart, for example, on a single interaction so we need to skip one of them if both are called within 100ms.\n\t\t\taltType = !_isUndefined(e.onpointerdown) ? \"pointerdown\" : !_isUndefined(e.ontouchstart) ? \"touchstart\" : null;\n\t\t\tif (altType) {\n\t\t\t\thandler = event => {\n\t\t\t\t\tif (event.target.nodeName.toLowerCase() !== \"select\" && event.type === altType) { //don't preventDefault() on a up or down by a certain amount.\n\t_shiftSelectedValue = (element, amount, label) => {\n\t\tlet options = element.options,\n\t\t\ti = Math.min(options.length - 1, Math.max(0, element.selectedIndex + amount));\n\t\telement.selectedIndex = i;\n\t\tif (label) {\n\t\t\tlabel.innerHTML = options[i].innerHTML;\n\t\t}\n\t\treturn options[i].value;\n\t},\n\t//moves everything from _globalTimeline into _recordedRoot and updates the _rootTween if it is currently controlling the Global timeline (_recordedRoot). _recordedTemp is just a temporary recording area for anything that happens while _recordedRoot is paused. Returns true if the _recordedRoot's duration changed due to the merge.\n\t_merge = () => {\n\t\tlet t = _globalTimeline._first,\n\t\t\tduration, next, target;\n\t\tif (_rootInstance) {\n\t\t\tduration = _recordedRoot._dur;\n\t\t\twhile (t) {\n\t\t\t\tnext = t._next;\n\t\t\t\ttarget = t._targets && t._targets[0];\n\t\t\t\tif (!(_isFunction(target) && target === t.vars.onComplete && !t._dur) && !(target && target._gsIgnore)) { //typically, delayedCalls aren't included in the _recordedTemp, but since the hijacked add() below fires BEFORE TweenLite's constructor sets the target, we couldn't check that target === vars.onComplete there. And Draggable creates a tween with just an onComplete (no onReverseComplete), thus it fails that test. Therefore, we test again here to avoid merging that in.\n\t\t\t\t\t_recordedRoot.add(t, t._start - t._delay);\n\t\t\t\t}\n\t\t\t\tt = next;\n\t\t\t}\n\t\t\treturn (duration !== _recordedRoot.duration());\n\t\t}\n\t},\n\t_updateRootDuration = () => {\n\t\tif (_rootInstance) {\n\t\t\t_rootInstance.update();\n\t\t\t_rootIsDirty = false;\n\t\t}\n\t\tgsap.ticker.remove(_updateRootDuration);\n\t},\n\n\t_buildPlayPauseMorph = svg => {\n\t\tlet tl = gsap.timeline({data:\"root\", parent:_independentRoot, onComplete:() => tl.kill() }, _independentRoot._time);\n\t\ttl.to(svg.querySelector(\".play-1\"), {duration:0.4, attr:{d:\"M5.75,3.13 C5.75,9.79 5.75,16.46 5.75,23.13 4.08,23.13 2.41,23.13 0.75,23.13 0.75,16.46 0.75,9.79 0.75,3.12 2.41,3.12 4.08,3.12 5.75,3.12\"}, ease:\"power2.inOut\", rotation:360, transformOrigin:\"50% 50%\"})\n\t\t .to(svg.querySelector(\".play-2\"), {duration:0.4, attr:{d:\"M16.38,3.13 C16.38,9.79 16.38,16.46 16.38,23.13 14.71,23.13 13.04,23.13 11.38,23.13 11.38,16.46 11.38,9.79 11.38,3.12 13.04,3.12 14.71,3.12 16.38,3.12\"}, ease:\"power2.inOut\", rotation:360, transformOrigin:\"50% 50%\"}, 0.05);\n\t\treturn tl;\n\t},\n\n\t_buildLoopAnimation = svg => {\n\t\tlet tl = gsap.timeline({data:\"root\", id:\"loop\", parent:_independentRoot, paused:true, onComplete:() => tl.kill() }, _independentRoot._time);\n\t\ttl.to(svg, {duration: 0.5, rotation:360, ease:\"power3.inOut\", transformOrigin:\"50% 50%\"})\n\t\t .to(svg.querySelectorAll(\".loop-path\"), {duration:0.5, fill:\"#91e600\", ease:\"none\"}, 0);\n\t\treturn tl;\n\t},\n\n\t_getAnimationById = id => gsap.getById(id) || _independentRoot.getById(id) || (id === _recordedRoot.vars.id && _recordedRoot),\n\n\n\n\t_initCore = core => {\n\t\tgsap = core || _getGSAP();\n\t\tif (!_coreInitted) {\n\t\t\tif (gsap && _windowExists()) {\n\t\t\t\t_doc = document;\n\t\t\t\t_docEl = _doc.documentElement;\n\t\t\t\t_win = window;\n\t\t\t\t_context = gsap.core.context || function() {};\n\t\t\t\tgsap.registerPlugin(Draggable);\n\t\t\t\t_globalTimeline = gsap.globalTimeline;\n\t\t\t\t_globalTimeline._sort = true;\n\t\t\t\t_globalTimeline.autoRemoveChildren = false;\n\t\t\t\tAnimation = gsap.core.Animation;\n\t\t\t\t_independentRoot = gsap.timeline({data:\"indy\", autoRemoveChildren:true, smoothChildTiming:true});\n\t\t\t\t_independentRoot.kill();\n\t\t\t\t_independentRoot._dp = 0; //don't let it revert to the global timeline as its parent.\n\t\t\t\t_independentRoot.to({}, {duration:1e12});\n\t\t\t\t_recordedRoot = gsap.timeline({data:\"root\", id:\"Global Timeline\", autoRemoveChildren:false, smoothChildTiming:true, parent:_independentRoot}, 0);\n\t\t\t\t_rootTween = gsap.to(_recordedRoot, {duration:1, time:1, ease:\"none\", data:\"root\", id:\"_rootTween\", paused:true, immediateRender:false, parent:_independentRoot}, 0);\n\t\t\t\t// so that auto-overwriting works. Initially we transferred the tweens to the _recordedRoot.\n\t\t\t\t_globalTimeline.killTweensOf = function(targets, props, onlyActive) {\n\t\t\t\t\t_recordedRoot.killTweensOf(targets, props, onlyActive);\n\t\t\t\t\t_recordedRoot.killTweensOf.call(_globalTimeline, targets, props, onlyActive);\n\t\t\t\t}\n\t\t\t\t_independentRoot._start = gsap.ticker.time;\n\t\t\t\tgsap.ticker.add(time => _independentRoot.render(time - _independentRoot._start));\n\t\t\t\t// before 3.7.0, the listener below was necessary (in place of the line above)\n\t\t\t\t// gsap.ticker.add(time => {\n\t\t\t\t// \t_independentRoot._initted || _independentRoot.render(0.001, true); // to prevent callbacks from being fired on the first tick, like onComplete would fire immediately and then again at the correct time.\n\t\t\t\t// \t_independentRoot.render(time - _independentRoot._start);\n\t\t\t\t// });\n\n\t\t\t\t//align the all of the playheads so they're starting at 0 now.\n\t\t\t\t_globalTimeline._start += _globalTimeline._time;\n\t\t\t\t_recordedRoot._start = _globalTimeline._time = _globalTimeline._tTime = 0;\n\t\t\t\t_delayedCall = (delay, callback, params, scope) => gsap.to(callback, {delay:delay, duration:0, onComplete:callback, onReverseComplete:callback, onCompleteParams:params, onReverseCompleteParams:params, callbackScope:scope, parent:_independentRoot}, _independentRoot._time);\n\n\t\t\t\t//in case GSDevTools.create() is called before anything is actually on the global timeline, we've gotta update it or else the duration will be 0 and it'll be stuck.\n\t\t\t\t_delayedCall(0.01, () => _rootInstance ? _rootInstance.update() : _merge());\n\n\t\t\t\t//initially we record everything into the _recordedRoot Timeline because developers might call GSDevTools.create() AFTER some of their code executes, but after 2 seconds if there aren't any GSDevTool instances that have globalSync enabled, we should dump all the stuff from _recordedRoot into the global timeline to improve performance and avoid issues where _recordedRoot is paused and reaches its end and wants to stop the playhead.\n\t\t\t\t_delayedCall(2, () => {\n\t\t\t\t\tlet t, next, offset;\n\t\t\t\t\tif (!_rootInstance) {\n\t\t\t\t\t\t_merge();\n\t\t\t\t\t\tt = _recordedRoot._first;\n\t\t\t\t\t\toffset = _recordedRoot._start;\n\t\t\t\t\t\twhile (t) {\n\t\t\t\t\t\t\tnext = t._next;\n\t\t\t\t\t\t\t//any animations that aren't finished should be dumped into the root timeline. If they're done, just kill them.\n\t\t\t\t\t\t\tif (t._tDur !== t._tTime || (!t._dur && t.progress() !== 1)) {\n\t\t\t\t\t\t\t\t_globalTimeline.add(t, t._start - t._delay + offset);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tt.kill();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tt = next;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif (GSDevTools.globalRecordingTime > 2) {\n\t\t\t\t\t\t_delayedCall(GSDevTools.globalRecordingTime - 2, () => {\n\t\t\t\t\t\t\t_rootInstance && _rootInstance.update();\n\t\t\t\t\t\t\t_recording = false;\n\t\t\t\t\t\t\t_globalTimeline.autoRemoveChildren = true;\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\t_recording = false;\n\t\t\t\t\t\t_globalTimeline.autoRemoveChildren = true;\n\t\t\t\t\t}\n\t\t\t\t\t_startupPhase = false;\n\t\t\t\t});\n\t\t\t\t_coreInitted = 1;\n\t\t\t}\n\t\t}\n\t},\n\t_checkIndependence = (animation, vars) => {\n\t\tif (!vars.globalSync && animation.parent !== _globalTimeline) { //in case it's nested in a timeline (playing it won't help if the parent timeline isn't playing).\n\t\t\t_globalTimeline.add(animation, _globalTimeline.time());\n\t\t}\n\t},\n\n\n\n\n\n\n\n\tGSDevTools = function(vars) {\n\t\tif (!_coreInitted) {\n\t\t\t_initCore();\n\t\t\tgsap || console.warn(\"Please gsap.registerPlugin(GSDevTools)\");\n\t\t}\n\n\t\tthis.vars = vars = vars || {};\n\t\tif (vars.animation) {\n\t\t\t(GSDevTools.getByAnimation(vars.animation) || {kill:() => 0}).kill();\n\t\t}\n\t\tvars.id = vars.id || (_isString(vars.animation) ? vars.animation : _idSeed++); //try to find a unique ID so that sessionStorage can be mapped to it (otherwise, for example, all the embedded codepens on a page would share the same settings). So if no id is defined, see if there's a string-based \"animation\" defined. Last of all, we default to a numeric counter that we increment.\n\t\t_lookup[vars.id + \"\"] = this;\n\n\t\t(\"globalSync\" in vars) || (vars.globalSync = !vars.animation); //if the user calls create() and passes in an animation AFTER the initial recording time has elapsed, there's a good chance the animation won't be in the recordedRoot, so we change the default globalSync to false because that's the most intuitive behavior.\n\n\t\t//GENERAL/UTILITY\n\t\tlet _self = this,\n\t\t\troot = _createRootElement(vars.container, vars.minimal, vars.css),\n\t\t\tfind = s => root.querySelector(s),\n\t\t\trecord = (key, value) => {\n\t\t\t\tif (vars.persist !== false && _supportsStorage) {\n\t\t\t\t\tsessionStorage.setItem(\"gs-dev-\" + key + vars.id, value);\n\t\t\t\t}\n\t\t\t\treturn value;\n\t\t\t},\n\t\t\trecall = key => {\n\t\t\t\tlet value;\n\t\t\t\tif (vars.persist !== false && _supportsStorage) {\n\t\t\t\t\tvalue = sessionStorage.getItem(\"gs-dev-\" + key + vars.id);\n\t\t\t\t\treturn (key === \"animation\") ? value : (key === \"loop\") ? (value === \"true\") : parseFloat(value); // handle data typing too.\n\t\t\t\t}\n\t\t\t},\n\n\n\t\t\t//SCRUBBER/PROGRESS\n\t\t\tplayhead = find(\".playhead\"),\n\t\t\ttimelineTrack = find(\".timeline-track\"),\n\t\t\tprogressBar = find(\".progress-bar\"),\n\t\t\ttimeLabel = find(\".time\"),\n\t\t\tdurationLabel = find(\".duration\"),\n\t\t\tpixelToTimeRatio, timeAtDragStart, dragged, skipDragUpdates,\n\t\t\tprogress = 0,\n\t\t\tinPoint = find(\".in-point\"),\n\t\t\toutPoint = find(\".out-point\"),\n\t\t\tinProgress = 0,\n\t\t\toutProgress = 100,\n\t\t\tpausedWhenDragStarted,\n\t\t\tlist = find(\".animation-list\"),\n\t\t\tanimationLabel = find(\".animation-label\"),\n\t\t\tselectedAnimation, //the currently selected animation\n\t\t\tlinkedAnimation, //the animation that's linked to all the controls and scrubber. This is always _rootTween if globalSync is true, so it can be different than the selectedAnimation!\n\t\t\tdeclaredAnimation, //whatever the user defines in the config object initially (often this will be null). If the user defines a string, it'll be resolved to a real Animation instance for this variable.\n\t\t\tstartTime, endTime,\n\t\t\t_fullyInitialized, //we call initialize() initially, and then again on the very next tick just in case someone called GSDevTools.create() BEFORE they create their animations. This variable tracks that state. Note: we don't record sessionStorage.setItem() until we're fully initialized, otherwise we may inadvertently set in/out points to the defaults just because the animation couldn't be found (yet).\n\t\t\tkeyboardHandler,\n\t\t\tplayPauseButton = find(\".play-pause\"),\n\t\t\tplayPauseMorph = _buildPlayPauseMorph(playPauseButton),\n\t\t\tpaused = false,\n\t\t\tloopButton = find(\".loop\"),\n\t\t\tloopAnimation = _buildLoopAnimation(loopButton),\n\t\t\tloopEnabled,\n\t\t\ttimeScale = find(\".time-scale select\"),\n\t\t\ttimeScaleLabel = find(\".time-scale-label\"),\n\t\t\t//spits back a common onPress function for anything that's dragged along the timeline (playhead, inPoint, outPoint). The originRatio is a value from 0-1 indicating how far along the x-axis the origin is located (0.5 is in the center, 0 is left, 1 is on right side). limitElement is optional, and sets the bounds such that the element can't be dragged past the limitElement.\n\t\t\tonPressTimeline = (element, originRatio, limitToInOut) => {\n\t\t\t\treturn function(e) {\n\t\t\t\t\tlet trackBounds = timelineTrack.getBoundingClientRect(),\n\t\t\t\t\t\telementBounds = element.getBoundingClientRect(),\n\t\t\t\t\t\tleft = elementBounds.width * originRatio,\n\t\t\t\t\t\tx = gsap.getProperty(element, \"x\"),\n\t\t\t\t\t\tminX = trackBounds.left - elementBounds.left - left + x,\n\t\t\t\t\t\tmaxX = trackBounds.right - elementBounds.right + (elementBounds.width - left) + x,\n\t\t\t\t\t\tunlimitedMinX = minX,\n\t\t\t\t\t\tlimitBounds;\n\t\t\t\t\tif (limitToInOut) {\n\t\t\t\t\t\tif (element !== inPoint) {\n\t\t\t\t\t\t\tlimitBounds = inPoint.getBoundingClientRect();\n\t\t\t\t\t\t\tif (limitBounds.left) { //if inPoint is hidden (like display:none), ignore.\n\t\t\t\t\t\t\t\tminX += (limitBounds.left + limitBounds.width) - trackBounds.left;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (element !== outPoint) {\n\t\t\t\t\t\t\tlimitBounds = outPoint.getBoundingClientRect();\n\t\t\t\t\t\t\tif (limitBounds.left) { //if outPoint is hidden (like display:none), ignore.\n\t\t\t\t\t\t\t\tmaxX -= (trackBounds.left + trackBounds.width) - limitBounds.left;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpausedWhenDragStarted = paused;\n\t\t\t\t\tthis.applyBounds({minX:minX, maxX:maxX});\n\t\t\t\t\tpixelToTimeRatio = linkedAnimation.duration() / trackBounds.width;\n\t\t\t\t\ttimeAtDragStart = -unlimitedMinX * pixelToTimeRatio;\n\t\t\t\t\tif (!skipDragUpdates) {\n\t\t\t\t\t\tlinkedAnimation.pause(timeAtDragStart + pixelToTimeRatio * this.x);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlinkedAnimation.pause();\n\t\t\t\t\t}\n\t\t\t\t\tif (this.target === playhead) {\n\t\t\t\t\t\tif (this.activated) {\n\t\t\t\t\t\t\tthis.allowEventDefault = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis.activated = true;\n\t\t\t\t\t}\n\t\t\t\t\tdragged = true;\n\t\t\t\t};\n\t\t\t},\n\t\t\tprogressDrag = Draggable.create(playhead, {\n\t\t\t\ttype:\"x\",\n\t\t\t\tcursor: \"ew-resize\",\n\t\t\t\tallowNativeTouchScrolling: false,\n\t\t\t\tallowEventDefault: true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem \"stuck\" to the mouse.\n\t\t\t\tonPress: onPressTimeline(playhead, 0.5, true),\n\t\t\t\tonDrag: function() {\n\t\t\t\t\tlet time = timeAtDragStart + pixelToTimeRatio * this.x;\n\t\t\t\t\tif (time < 0) {\n\t\t\t\t\t\ttime = 0;\n\t\t\t\t\t} else if (time > linkedAnimation._dur) {\n\t\t\t\t\t\ttime = linkedAnimation._dur;\n\t\t\t\t\t}\n\t\t\t\t\tif (!skipDragUpdates) {\n\t\t\t\t\t\tlinkedAnimation.time(time);\n\t\t\t\t\t}\n\t\t\t\t\tprogressBar.style.width = Math.min(outProgress - inProgress, Math.max(0, (time / linkedAnimation._dur) * 100 - inProgress)) + \"%\";\n\t\t\t\t\ttimeLabel.innerHTML = time.toFixed(2);\n\t\t\t\t},\n\t\t\t\tonRelease: function() {\n\t\t\t\t\tpaused || linkedAnimation.resume();\n\t\t\t\t}\n\t\t\t})[0],\n\t\t\tresetInOut = () => {\n\t\t\t\tinProgress = 0;\n\t\t\t\toutProgress = 100;\n\t\t\t\tinPoint.style.left = \"0%\";\n\t\t\t\toutPoint.style.left = \"100%\";\n\t\t\t\trecord(\"in\", inProgress);\n\t\t\t\trecord(\"out\", outProgress);\n\t\t\t\tupdateProgress(true);\n\t\t\t},\n\t\t\tinDrag = Draggable.create(inPoint, {\n\t\t\t\ttype: \"x\",\n\t\t\t\tcursor: \"ew-resize\",\n\t\t\t\tzIndexBoost: false,\n\t\t\t\tallowNativeTouchScrolling: false,\n\t\t\t\tallowEventDefault: true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem \"stuck\" to the mouse.\n\t\t\t\tonPress: onPressTimeline(inPoint, 1, true),\n\t\t\t\tonDoubleClick: resetInOut,\n\t\t\t\tonDrag: function() {\n\t\t\t\t\tinProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100;\n\t\t\t\t\tlinkedAnimation.progress(inProgress / 100);\n\t\t\t\t\tupdateProgress(true);\n\t\t\t\t},\n\t\t\t\tonRelease: function() {\n\t\t\t\t\tif (inProgress < 0) {\n\t\t\t\t\t\tinProgress = 0;\n\t\t\t\t\t}\n\t\t\t\t\t_clearSelection();\n\t\t\t\t\t//for responsiveness, convert the px-based transform into %-based left position.\n\t\t\t\t\tinPoint.style.left = inProgress + \"%\";\n\t\t\t\t\trecord(\"in\", inProgress);\n\t\t\t\t\tgsap.set(inPoint, {x:0, data:\"root\", display:\"block\"}); //set display:block so that it remains visible even when the minimal skin is enabled.\n\t\t\t\t\tif (!paused) {\n\t\t\t\t\t\tlinkedAnimation.resume();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})[0],\n\t\t\toutDrag = Draggable.create(outPoint, {\n\t\t\t\ttype: \"x\",\n\t\t\t\tcursor: \"ew-resize\",\n\t\t\t\tallowNativeTouchScrolling: false,\n\t\t\t\tallowEventDefault: true, //otherwise, when dragged outside an iframe, the mouseup doesn't bubble up so it could seem \"stuck\" to the mouse.\n\t\t\t\tzIndexBoost: false,\n\t\t\t\tonPress: onPressTimeline(outPoint, 0, true),\n\t\t\t\tonDoubleClick: resetInOut,\n\t\t\t\tonDrag: function() {\n\t\t\t\t\toutProgress = (timeAtDragStart + pixelToTimeRatio * this.x) / linkedAnimation.duration() * 100;\n\t\t\t\t\tlinkedAnimation.progress(outProgress / 100);\n\t\t\t\t\tupdateProgress(true);\n\t\t\t\t},\n\t\t\t\tonRelease: function() {\n\t\t\t\t\tif (outProgress > 100) {\n\t\t\t\t\t\toutProgress = 100;\n\t\t\t\t\t}\n\t\t\t\t\t_clearSelection();\n\t\t\t\t\t//for responsiveness, convert the px-based transform into %-based left position.\n\t\t\t\t\toutPoint.style.left = outProgress + \"%\";\n\t\t\t\t\trecord(\"out\", outProgress);\n\t\t\t\t\tgsap.set(outPoint, {x:0, data:\"root\", display:\"block\"}); //set display:block so that it remains visible even when the minimal skin is enabled.\n\t\t\t\t\tif (!pausedWhenDragStarted) {\n\t\t\t\t\t\tplay();\n\t\t\t\t\t\tlinkedAnimation.resume();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t})[0],\n\t\t\tupdateProgress = function(force) { // NOTE: \"force\" is actually the \"time\" when this method gets called by the gsap.ticker!\n\t\t\t\tif (progressDrag.isPressed && force !== true) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet p = (!loopEnabled && selectedAnimation._repeat === -1) ? selectedAnimation.totalTime() / selectedAnimation.duration() * 100 : (linkedAnimation.progress() * 100) || 0,\n\t\t\t\t\trepeatDelayPhase = (selectedAnimation._repeat && selectedAnimation._rDelay && selectedAnimation.totalTime() % (selectedAnimation.duration() + selectedAnimation._rDelay) > selectedAnimation.duration()),\n\t\t\t\t\ttarget;\n\t\t\t\tif (p > 100) {\n\t\t\t\t\tp = 100;\n\t\t\t\t}\n\t\t\t\tif (p >= outProgress) {\n\t\t\t\t\tif (loopEnabled && !linkedAnimation.paused() && !progressDrag.isDragging) {\n\t\t\t\t\t\tif (!repeatDelayPhase) {\n\t\t\t\t\t\t\tp = inProgress;\n\t\t\t\t\t\t\ttarget = linkedAnimation._targets && linkedAnimation._targets[0];\n\t\t\t\t\t\t\tif (target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them.\n\t\t\t\t\t\t\t\ttarget.seek(startTime + ((endTime - startTime) * inProgress / 100));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (selectedAnimation._repeat > 0 && !inProgress && outProgress === 100) {\n\t\t\t\t\t\t\t\tif (selectedAnimation.totalProgress() === 1) {\n\t\t\t\t\t\t\t\t\tlinkedAnimation.totalProgress(0, true).resume();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tlinkedAnimation.progress(p / 100, true).resume();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (p !== outProgress || selectedAnimation._repeat === -1) {\n\t\t\t\t\t\t\tp = outProgress;\n\t\t\t\t\t\t\tlinkedAnimation.progress(p / 100);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (!paused && (outProgress < 100 || selectedAnimation.totalProgress() === 1 || selectedAnimation._repeat === -1)) {\n\t\t\t\t\t\t\tpause();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t} else if (p < inProgress) {\n\t\t\t\t\tp = inProgress;\n\t\t\t\t\tlinkedAnimation.progress(p / 100, true);\n\t\t\t\t}\n\t\t\t\tif (p !== progress || force === true) {\n\t\t\t\t\tprogressBar.style.left = inProgress + \"%\";\n\t\t\t\t\tprogressBar.style.width = Math.max(0, p - inProgress) + \"%\";\n\t\t\t\t\tplayhead.style.left = p + \"%\";\n\t\t\t\t\ttimeLabel.innerHTML = linkedAnimation._time.toFixed(2);\n\t\t\t\t\tdurationLabel.innerHTML = linkedAnimation._dur.toFixed(2);\n\t\t\t\t\tif (dragged) {\n\t\t\t\t\t\tplayhead.style.transform = \"translate(-50%,0)\";\n\t\t\t\t\t\tplayhead._gsap.x = \"0px\";\n\t\t\t\t\t\tplayhead._gsap.xPercent = -50;\n\t\t\t\t\t\tdragged = false;\n\t\t\t\t\t}\n\t\t\t\t\tprogress = p;\n\t\t\t\t}\n\t\t\t\tlinkedAnimation.paused() !== paused && togglePlayPause(); // if the user has an addPause() in the middle of the animation.\n\t\t\t},\n\t\t\tonPressSeekBar = function(e) {\n\t\t\t\tif (progressDrag.isPressed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlet bounds = e.target.getBoundingClientRect(),\n\t\t\t\t\tx = (e.changedTouches ? e.changedTouches[0] : e).clientX,\n\t\t\t\t\tp = ((x - bounds.left) / bounds.width) * 100;\n\t\t\t\tif (p < inProgress) {\n\t\t\t\t\tinProgress = p = Math.max(0, p);\n\t\t\t\t\tinPoint.style.left = inProgress + \"%\";\n\t\t\t\t\tinDrag.startDrag(e);\n\t\t\t\t\treturn;\n\t\t\t\t} else if (p > outProgress) {\n\t\t\t\t\toutProgress = p = Math.min(100, p);\n\t\t\t\t\toutPoint.style.left = outProgress + \"%\";\n\t\t\t\t\toutDrag.startDrag(e);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlinkedAnimation.progress(p / 100).pause();\n\t\t\t\tupdateProgress(true);\n\t\t\t\tprogressDrag.startDrag(e);\n\t\t\t},\n\n\n\n\t\t\t//PLAY/PAUSE button\n\t\t\tplay = () => {\n\t\t\t\tif (linkedAnimation.progress() >= outProgress / 100) {\n\t\t\t\t\t_checkIndependence(linkedAnimation, vars);\n\t\t\t\t\tlet target = linkedAnimation._targets && linkedAnimation._targets[0];\n\t\t\t\t\tif (target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them.\n\t\t\t\t\t\ttarget.seek(startTime + ((endTime - startTime) * inProgress / 100));\n\t\t\t\t\t}\n\t\t\t\t\tif (linkedAnimation._repeat && !inProgress) {\n\t\t\t\t\t\tlinkedAnimation.totalProgress(0, true); //for repeating animations, don't get stuck in the last iteration - jump all the way back to the start.\n\t\t\t\t\t} else if (!linkedAnimation.reversed()) {\n\t\t\t\t\t\tlinkedAnimation.progress(inProgress / 100, true);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tplayPauseMorph.play();\n\t\t\t\tlinkedAnimation.resume();\n\t\t\t\tif (paused) {\n\t\t\t\t\t_self.update();\n\t\t\t\t}\n\t\t\t\tpaused = false;\n\t\t\t},\n\t\t\tpause = () => {\n\t\t\t\tplayPauseMorph.reverse();\n\t\t\t\tif (linkedAnimation) {\n\t\t\t\t\tlinkedAnimation.pause();\n\t\t\t\t}\n\t\t\t\tpaused = true;\n\t\t\t},\n\t\t\ttogglePlayPause = () => {\n\t\t\t\tif (paused) {\n\t\t\t\t\tplay();\n\t\t\t\t} else {\n\t\t\t\t\tpause();\n\t\t\t\t}\n\t\t\t},\n\n\n\n\t\t\t//REWIND button\n\t\t\tonPressRewind = e => {\n\t\t\t\tif (progressDrag.isPressed) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\t//_self.update();\n\t\t\t\t_checkIndependence(linkedAnimation, vars);\n\t\t\t\tlet target = linkedAnimation._targets && linkedAnimation._targets[0];\n\t\t\t\tif (target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them.\n\t\t\t\t\ttarget.seek(startTime + ((endTime - startTime) * inProgress / 100));\n\t\t\t\t}\n\t\t\t\tlinkedAnimation.progress(inProgress / 100, true);\n\t\t\t\tif (!paused) {\n\t\t\t\t\tlinkedAnimation.resume();\n\t\t\t\t}\n\t\t\t},\n\n\n\n\t\t\t//LOOP button\n\t\t\tloop = value => {\n\t\t\t\tloopEnabled = value;\n\t\t\t\trecord(\"loop\", loopEnabled);\n\t\t\t\tif (loopEnabled) {\n\t\t\t\t\tloopAnimation.play();\n\t\t\t\t\tif (linkedAnimation.progress() >= outProgress / 100) {\n\t\t\t\t\t\tlet target = linkedAnimation._targets && linkedAnimation._targets[0];\n\t\t\t\t\t\tif (target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them.\n\t\t\t\t\t\t\ttarget.seek(startTime + ((endTime - startTime) * inProgress / 100));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (selectedAnimation._repeat && !inProgress && outProgress === 100) {\n\t\t\t\t\t\t\tlinkedAnimation.totalProgress(0, true);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlinkedAnimation.progress(inProgress / 100, true);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tplay();\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tloopAnimation.reverse();\n\t\t\t\t}\n\t\t\t},\n\n\t\t\ttoggleLoop = () => loop(!loopEnabled),\n\n\n\n\t\t\t//ANIMATIONS list\n\t\t\tupdateList = () => {\n\t\t\t\tlet animations = _getChildrenOf((declaredAnimation && !vars.globalSync) ? declaredAnimation : _recordedRoot, true),\n\t\t\t\t\toptions = list.children,\n\t\t\t\t\tmatches = 0,\n\t\t\t\t\toption,\ti;\n\t\t\t\tif (declaredAnimation && !vars.globalSync) {\n\t\t\t\t\tanimations.unshift(declaredAnimation);\n\t\t\t\t} else if (!vars.hideGlobalTimeline) {\n\t\t\t\t\tanimations.unshift(_recordedRoot);\n\t\t\t\t}\n\t\t\t\tfor (i = 0; i < animations.length; i++) {\n\t\t\t\t\toption = options[i] || _createElement(\"option\", list);\n\t\t\t\t\toption.animation = animations[i];\n\t\t\t\t\tmatches = (i && animations[i].vars.id === animations[i-1].vars.id) ? matches + 1 : 0;\n\t\t\t\t\toption.setAttribute(\"value\", (option.innerHTML = animations[i].vars.id + (matches ? \" [\" + matches + \"]\" : (animations[i+1] && animations[i+1].vars.id === animations[i].vars.id) ? \" [0]\" : \"\")));\n\t\t\t\t}\n\t\t\t\tfor (; i < options.length; i++) {\n\t\t\t\t\tlist.removeChild(options[i]);\n\t\t\t\t}\n\t\t\t},\n\t\t\tanimation = function(anim) {\n\t\t\t\tlet ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1,\n\t\t\t\t\ttl, maxDuration;\n\t\t\t\tif (!arguments.length) {\n\t\t\t\t\treturn selectedAnimation;\n\t\t\t\t}\n\t\t\t\tif (_isString(anim)) {\n\t\t\t\t\tanim = _getAnimationById(anim);\n\t\t\t\t}\n\t\t\t\t//console.log(\"animation() \", anim.vars.id);\n\t\t\t\tif (!(anim instanceof Animation)) {\n\t\t\t\t\tconsole.warn(\"GSDevTools error: invalid animation.\");\n\t\t\t\t}\n\t\t\t\tif (anim.scrollTrigger) {\n\t\t\t\t\tconsole.warn(\"GSDevTools can't work with ScrollTrigger-based animations; either the scrollbar -OR- the GSDevTools scrubber can control the animation.\");\n\t\t\t\t}\n\t\t\t\tif (anim === selectedAnimation) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (selectedAnimation) {\n\t\t\t\t\tselectedAnimation._inProgress = inProgress;\n\t\t\t\t\tselectedAnimation._outProgress = outProgress;\n\t\t\t\t}\n\t\t\t\tselectedAnimation = anim;\n\t\t\t\tif (linkedAnimation) {\n\t\t\t\t\tts = linkedAnimation.timeScale();\n\t\t\t\t\tif (linkedAnimation._targets && linkedAnimation._targets[0] === declaredAnimation) {\n\t\t\t\t\t\tdeclaredAnimation.resume();\n\t\t\t\t\t\tlinkedAnimation.kill();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tinProgress = selectedAnimation._inProgress || 0;\n\t\t\t\toutProgress = selectedAnimation._outProgress || 100;\n\t\t\t\tinPoint.style.left = inProgress + \"%\";\n\t\t\t\toutPoint.style.left = outProgress + \"%\";\n\t\t\t\tif (_fullyInitialized) { //don't record inProgress/outProgress unless we're fully instantiated because people may call GSDevTools.create() before creating/defining their animations, thus the inTime/outTime may not exist yet.\n\t\t\t\t\trecord(\"animation\", selectedAnimation.vars.id);\n\t\t\t\t\trecord(\"in\", inProgress);\n\t\t\t\t\trecord(\"out\", outProgress);\n\t\t\t\t}\n\t\t\t\tstartTime = 0;\n\t\t\t\tmaxDuration = vars.maxDuration || Math.min(1000, _getClippedDuration(selectedAnimation));\n\t\t\t\tif (selectedAnimation === _recordedRoot || vars.globalSync) {\n\t\t\t\t\t_merge();\n\t\t\t\t\tlinkedAnimation = _rootTween;\n\t\t\t\t\t_rootInstance && _rootInstance !== _self && console.warn(\"Error: GSDevTools can only have one instance that's globally synchronized.\");\n\t\t\t\t\t_rootInstance = _self;\n\t\t\t\t\tif (selectedAnimation !== _recordedRoot) {\n\t\t\t\t\t\ttl = selectedAnimation;\n\t\t\t\t\t\tendTime = tl.totalDuration();\n\t\t\t\t\t\tif (endTime > 99999999) { //in the case of an infinitely repeating animation, just use a single iteration's duration instead.\n\t\t\t\t\t\t\tendTime = tl.duration();\n\t\t\t\t\t\t}\n\t\t\t\t\t\twhile (tl.parent) {\n\t\t\t\t\t\t\tstartTime = (startTime / tl._ts) + tl._start;\n\t\t\t\t\t\t\tendTime = (endTime / tl._ts) + tl._start;\n\t\t\t\t\t\t\ttl = tl.parent;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tendTime = _recordedRoot.duration();\n\t\t\t\t\t}\n\t\t\t\t\tif (endTime - startTime > maxDuration) { //cap end time at 1000 because it doesn't seem reasonable to accommodate super long stuff.\n\t\t\t\t\t\tendTime = startTime + maxDuration;\n\t\t\t\t\t}\n\t\t\t\t\t_recordedRoot.pause(startTime);\n\t\t\t\t\t_rootTween.vars.time = endTime;\n\t\t\t\t\t_rootTween.invalidate();\n\t\t\t\t\t_rootTween.duration(endTime - startTime).timeScale(ts);\n\t\t\t\t\t//wait for a tick before starting because some browsers freeze things immediately following a .\n\t\t\t\t\te.target.blur();\n\t\t\t\t}\n\t\t\t\tpaused && play();\n\t\t\t},\n\n\n\n\t\t\t//TIMESCALE button\n\t\t\tonChangeTimeScale = e => {\n\t\t\t\tlet ts = parseFloat(timeScale.options[timeScale.selectedIndex].value) || 1,\n\t\t\t\t\ttarget;\n\t\t\t\tlinkedAnimation.timeScale(ts);\n\t\t\t\trecord(\"timeScale\", ts);\n\t\t\t\tif (!paused) {\n\t\t\t\t\tif (linkedAnimation.progress() >= outProgress / 100) {\n\t\t\t\t\t\ttarget = linkedAnimation._targets && linkedAnimation._targets[0];\n\t\t\t\t\t\tif (target === selectedAnimation) { //in case there are callbacks on the timeline, when we jump back to the start we should seek() so that the playhead doesn't drag [backward] past those and trigger them.\n\t\t\t\t\t\t\ttarget.seek(startTime + ((endTime - startTime) * inProgress / 100));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tlinkedAnimation.progress(inProgress / 100, true).pause();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tlinkedAnimation.pause();\n\t\t\t\t\t}\n\t\t\t\t\t_delayedCall(0.01, () => linkedAnimation.resume());\n\t\t\t\t}\n\t\t\t\ttimeScaleLabel.innerHTML = ts + \"x\";\n\t\t\t\tif (timeScale.blur) { //so that if an option is selected, and then the user tries to hit the up/down arrow, it doesn't just try selecting something else in the