document.addEventListener("DOMContentLoaded", function() {
const filterInput = document.getElementById("filter-input");
const componentSelect = document.getElementById("component-filter-select");
const groupSelect = document.getElementById("group-filter-select");
let currentComponentFilter = componentSelect ? componentSelect.value : "";
let currentGroupFilter = groupSelect ? groupSelect.value : "";
function applyFilters() {
const filterValue = filterInput ? filterInput.value.trim().toLowerCase() : "";
const listItems = document.querySelectorAll(".function-list li");
listItems.forEach(li => {
const link = li.querySelector("a");
if (!link) return;
const text = link.textContent.toLowerCase();
const matchesText = text.includes(filterValue);
const componentAttr = li.getAttribute("data-component") || "";
const matchesComponent =
(currentComponentFilter === "" || componentAttr === currentComponentFilter);
const groupAttr = li.getAttribute("data-groupUuid") || "";
const matchesGroup =
(currentGroupFilter === "" || groupAttr === currentGroupFilter);
li.style.display = (matchesText && matchesComponent && matchesGroup) ? "" : "none";
});
const docBlocks = document.querySelectorAll(".doc-block");
docBlocks.forEach(block => {
const blockComponent = block.getAttribute("data-component") || "";
const blockGroup = block.getAttribute("data-groupUuid") || "";
const componentMatches = (currentComponentFilter === "" || blockComponent === currentComponentFilter);
const groupMatches = (currentGroupFilter === "" || blockGroup === currentGroupFilter);
const textContent = block.textContent.toLowerCase();
const textMatches = textContent.includes(filterValue);
if (componentMatches && groupMatches && textMatches) {
block.style.display = "";
} else {
block.style.display = "none";
}
});
}
if (filterInput) {
filterInput.addEventListener("input", applyFilters);
}
if (componentSelect) {
componentSelect.addEventListener("change", function() {
currentComponentFilter = this.value;
applyFilters();
});
}
if (groupSelect) {
groupSelect.addEventListener("change", function() {
currentGroupFilter = this.value;
applyFilters();
});
}
function scrollMainContentToId(elementId) {
const mainContent = document.querySelector(".main-content");
if (!mainContent) return;
const targetEl = document.getElementById(elementId);
if (!targetEl) return;
let offsetTop = targetEl.offsetTop - mainContent.offsetTop;
mainContent.scrollTo({
top: offsetTop,
behavior: "smooth"
});
}
const sidebarLinks = document.querySelectorAll(".function-list li a[href^='#']");
sidebarLinks.forEach(link => {
link.addEventListener("click", function(e) {
const hash = this.getAttribute("href").slice(1); if (hash) {
e.preventDefault();
scrollMainContentToId(hash);
}
});
});
if (location.hash && location.hash.length > 1) {
const initialHash = location.hash.substring(1); scrollMainContentToId(initialHash);
}
applyFilters();
});
window.highlightMuMuSyntax = function(code) {
code = code
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>');
const lines = code.split('\n');
function highlightNonComment(str) {
let html = str.replace(/"([^"\\]|\\.)*"/g, function(m) {
return `<span class="af-string">${m}</span>`;
});
html = html.replace(/\b-?\d+\b/g, function(m) {
return `<span class="af-number">${m}</span>`;
});
html = html.replace(/\b_\b/g, '<span class="af-placeholder">_</span>');
const builtins = [
'fn','map','eq','plus','sput','slog','sum','inc','keys','tap',
'upper','lower','multiply','prop','assoc','merge','select','cat',
'comp','range','filter','reduce','pluck','head','tail','take',
'some','none','flatten','push','fall','every','find','group',
'intersect','sort','union','each','length'
];
const builtinsPattern = new RegExp('\\b(' + builtins.join('|') + ')\\b', 'g');
html = html.replace(builtinsPattern, function(match) {
if (match === 'fn') {
return `<span class="af-keyword">${match}</span>`;
} else {
return `<span class="af-builtin">${match}</span>`;
}
});
html = html.replace(/=>/g, '<span class="af-operator">=></span>');
return html;
}
const processedLines = lines.map(function(line) {
const commentIndex = line.indexOf('//');
if (commentIndex >= 0) {
const nonComment = line.substring(0, commentIndex);
const comment = line.substring(commentIndex);
return highlightNonComment(nonComment) + '<span class="af-comment">' + comment + '</span>';
} else {
return highlightNonComment(line);
}
});
return processedLines.join('\n');
};
document.addEventListener("DOMContentLoaded", function() {
var codeBlocks = document.querySelectorAll('.code-block');
codeBlocks.forEach(function(block) {
var originalCode = block.textContent;
block.innerHTML = window.highlightMuMuSyntax(originalCode);
});
});
const marqueeText = "LAVA => ";
const marqueeContainer = document.getElementById("marquee-content");
const halfRepeatCount = 40;
let marqueeSpans = [];
let marqueeWidth = 0;
let containerWidth = 0;
let totalLetters = 0;
function createMarqueeContent() {
marqueeContainer.innerHTML = "";
marqueeSpans = [];
for (let b = 0; b < halfRepeatCount; b++) {
addBlock(b);
}
const firstHalf = [...marqueeSpans];
for (let b = 0; b < halfRepeatCount; b++) {
const offset = b * marqueeText.length;
for (let i = 0; i < marqueeText.length; i++) {
const original = firstHalf[offset + i];
const clone = original.cloneNode(true);
marqueeContainer.appendChild(clone);
marqueeSpans.push(clone);
}
}
}
function addBlock(blockIndex) {
for (let i = 0; i < marqueeText.length; i++) {
const letter = marqueeText[i];
const span = document.createElement("span");
span.className = "marquee-letter";
span.textContent = letter;
span.dataset.baseScale = "1.0";
span.dataset.blockIndex = blockIndex.toString();
span.dataset.letterIndex = i.toString();
marqueeContainer.appendChild(span);
marqueeSpans.push(span);
}
}
function measureDimensions() {
const container = document.getElementById("marquee-container");
containerWidth = container.offsetWidth;
marqueeWidth = marqueeContainer.scrollWidth;
totalLetters = marqueeSpans.length - 1;
}
createMarqueeContent();
measureDimensions();
window.addEventListener('resize', measureDimensions);
let cycleDuration = 24.0;
let timeInCycle = 0;
let direction = 1;
let lastTime = performance.now();
let cycleCount = 0;
function easeInOutSine(t) {
return 0.5 - 0.5 * Math.cos(Math.PI * t);
}
let wavePos = 0;
let waveDir = 1;
let waveSpeed = 0.125;
const waveRadius = 6;
const waveAmp = 0.4;
const waveSpacing = 0.7;
function computeMaxShift() {
return containerWidth - marqueeWidth;
}
let maxShift = 0;
function animateMarquee(now) {
const dt = (now - lastTime) / 1000;
lastTime = now;
measureDimensions();
maxShift = computeMaxShift();
let step = dt / cycleDuration;
timeInCycle += direction * step;
if (timeInCycle < 0) {
timeInCycle = 0;
direction = 1;
cycleCount++;
if (cycleCount >= 3) {
return;
}
} else if (timeInCycle > 1) {
timeInCycle = 1;
direction = -1;
}
const frac = easeInOutSine(timeInCycle);
const xPos = frac * maxShift;
marqueeContainer.style.transform = `translateX(${xPos}px)`;
wavePos += waveSpeed * waveDir * dt;
if (wavePos < 0) {
wavePos = 0;
waveDir = 1;
} else if (wavePos > totalLetters) {
wavePos = totalLetters;
waveDir = -1;
}
const nowSec = now / 1000;
for (let s of marqueeSpans) {
const blockIndex = parseInt(s.dataset.blockIndex);
const letterIndex = parseInt(s.dataset.letterIndex);
const baseScale = parseFloat(s.dataset.baseScale);
const colorFlowFrequency = 0.02;
const colorFlowBlockOffset = 0.5;
const colorPhase =
nowSec * colorFlowFrequency * 2.0 * Math.PI +
blockIndex * colorFlowBlockOffset;
const hueMin = 0;
const hueMax = 60;
const hueRange = hueMax - hueMin;
const hueCenter = (hueMin + hueMax) / 2;
const hue = hueCenter + (hueRange / 2) * Math.sin(colorPhase);
s.style.color = `hsl(${hue}, 80%, 60%)`;
const baseWaveFrequency = 0.4;
const baseWaveOffset = 0.04;
const baseWaveAmplitude = 0.4;
const globalIndex = blockIndex * marqueeText.length + letterIndex;
const wavePhase =
nowSec * baseWaveFrequency * 2.0 * Math.PI + globalIndex * baseWaveOffset;
const waveVal = Math.sin(wavePhase);
const waveScale = baseScale + baseWaveAmplitude * waveVal;
const dist = Math.abs(globalIndex - wavePos);
let rippleFactor = 1;
if (dist <= waveRadius) {
const ripplePhase = dist * waveSpacing;
const rippleVal = Math.cos(ripplePhase) * waveAmp;
rippleFactor = 1 + rippleVal;
}
const finalScale = waveScale * rippleFactor;
s.style.transform = `scale(${finalScale}, ${finalScale})`;
}
requestAnimationFrame(animateMarquee);
}
requestAnimationFrame(animateMarquee);