$(document).ready(function () { var paddingValue = 30, collapseHysteresis = 12, resizeScheduled = false; var requestFrame = function requestFrame(callback) { var frame = window.requestAnimationFrame || window.webkitRequestAnimationFrame; if (typeof frame === 'function') { return frame.call(window, callback); } return window.setTimeout(callback, 16); }; var getListContentWidth = function getListContentWidth(selector) { var $list = $('.lm-navbar ' + selector).first(), width = 0, gap = 0, marginLeft = 0, marginRight = 0; if (!$list.length) { return 0; } $list.children('li').each(function () { width += $(this).outerWidth(true) || 0; }); gap = parseFloat($list.css('column-gap')) || parseFloat($list.css('gap')) || 0; if ($list.children('li').length > 1) { width += gap * ($list.children('li').length - 1); } marginLeft = parseFloat($list.css('margin-left')) || 0; marginRight = parseFloat($list.css('margin-right')) || 0; return width + marginLeft + marginRight; }; var getRequiredNavbarWidth = function getRequiredNavbarWidth() { var $navbar = $('.lm-navbar').first(), $collapse = $navbar.find('.navbar-collapse').first(), wasCollapsed = $collapse.hasClass('navbar-collapsed'), requiredWidth; // Hidden collapsed children cannot be measured reliably. Expand only for this // synchronous layout read, then restore before the browser paints the frame. if (wasCollapsed) { $collapse.addClass('navbar-collapse').removeClass('navbar-collapsed'); $navbar.removeClass('navbar-collapsed-before'); } requiredWidth = ($('.lm-navbar .navbar-brand').first().outerWidth(true) || 0) + getListContentWidth('.navbar-left-block') + getListContentWidth('.navbar-right-block') + getListContentWidth('.lm-utility-nav') + paddingValue; if (wasCollapsed) { $collapse.removeClass('navbar-collapse').addClass('navbar-collapsed'); $navbar.addClass('navbar-collapsed-before'); } return requiredWidth; }; var getAvailableNavbarWidth = function getAvailableNavbarWidth() { var $container = $('.lm-navbar > .container, .lm-navbar > .container-fluid').first(); return ($container.length && $container.innerWidth()) || $(window).width(); }; var navbarResizerFunc = function navbarResizerFunc() { var viewportWidth = $(window).width(), requiredWidth, availableWidth, isCollapsed; if ($('body').hasClass('lm-site--authenticated')) { $('#navbar').addClass('navbar-collapse').removeClass('navbar-collapsed'); $('.lm-navbar').removeClass('navbar-collapsed-before'); return; } if (viewportWidth < 768) { $('#navbar').addClass('navbar-collapse').removeClass('navbar-collapsed'); $('.lm-navbar').removeClass('navbar-collapsed-before'); return; } requiredWidth = getRequiredNavbarWidth(); availableWidth = getAvailableNavbarWidth(); isCollapsed = $('#navbar').hasClass('navbar-collapsed'); if (requiredWidth > availableWidth) { $('#navbar').removeClass('navbar-collapse'); $('#navbar').addClass('navbar-collapsed'); $('.lm-navbar').addClass('navbar-collapsed-before'); } else if (!isCollapsed || requiredWidth + collapseHysteresis <= availableWidth) { $('#navbar').addClass('navbar-collapse'); $('#navbar').removeClass('navbar-collapsed'); $('.lm-navbar').removeClass('navbar-collapsed-before'); } }; var scheduleNavbarResize = function scheduleNavbarResize() { if (resizeScheduled) { return; } resizeScheduled = true; requestFrame(function () { resizeScheduled = false; navbarResizerFunc(); }); }; $(window).on('resize lm:font-scale-change', scheduleNavbarResize); $('.lm-navbar .navbar-brand img').one('load error', scheduleNavbarResize); if ($(window).width() >= 768) { scheduleNavbarResize(); } $('[data-toggle="tooltip"]').tooltip(); }); (function () { 'use strict'; var root = document.documentElement, themeStorageKey = 'lm-theme-preference', fontStorageKey = 'lm-font-scale', sidebarStorageKey = 'lm-sidebar-state', fontScales = [90, 100, 110, 120], themeButtons = [], fontDecreaseButton, fontResetButton, fontIncreaseButton, fontScaleIndicator, systemThemeQuery = getMediaQuery('(prefers-color-scheme: dark)'), finePointerQuery = getMediaQuery('(hover: hover) and (pointer: fine)'), reducedMotionQuery = getMediaQuery('(prefers-reduced-motion: reduce)'), currentTheme = getStoredTheme(), currentFontScale = getStoredFontScale(), scrollFrameScheduled = false, pointerFrameScheduled = false, pendingPointerSurface = null, pendingPointerX = 0, pendingPointerY = 0; function requestFrame(callback) { var frame = window.requestAnimationFrame || window.webkitRequestAnimationFrame; if (typeof frame === 'function') { return frame.call(window, callback); } return window.setTimeout(callback, 16); } function getMediaQuery(query) { if (typeof window.matchMedia !== 'function') { return null; } try { return window.matchMedia(query); } catch (error) { return null; } } function readStorage(key) { try { return window.localStorage ? window.localStorage.getItem(key) : null; } catch (error) { return null; } } function writeStorage(key, value) { try { if (window.localStorage) { window.localStorage.setItem(key, value); } } catch (error) { // The preference still applies for the current page when storage is unavailable. } } function isValidTheme(theme) { return theme === 'light' || theme === 'dark' || theme === 'signature'; } function getStoredTheme() { var storedTheme = readStorage(themeStorageKey); return isValidTheme(storedTheme) ? storedTheme : 'light'; } function getFontScaleIndex(scale) { var index; for (index = 0; index < fontScales.length; index += 1) { if (fontScales[index] === scale) { return index; } } return -1; } function getStoredFontScale() { var storedScale = parseInt(readStorage(fontStorageKey), 10); return getFontScaleIndex(storedScale) !== -1 ? storedScale : 100; } function getResolvedTheme(theme) { if (theme === 'signature') { return 'dark'; } if (theme !== 'system') { return theme; } return systemThemeQuery && systemThemeQuery.matches ? 'dark' : 'light'; } function updateThemeButtons() { var index, button; for (index = 0; index < themeButtons.length; index += 1) { button = themeButtons[index]; button.setAttribute( 'aria-pressed', button.getAttribute('data-lm-theme') === currentTheme ? 'true' : 'false' ); } } function applyTheme(theme, shouldStore) { var resolvedTheme, themeColor, sidebarThemeCurrent; if (!isValidTheme(theme)) { theme = 'light'; } currentTheme = theme; resolvedTheme = getResolvedTheme(currentTheme); root.setAttribute('data-lm-theme', currentTheme); root.setAttribute('data-lm-resolved-theme', resolvedTheme); themeColor = document.getElementById('lm-theme-color'); if (themeColor) { themeColor.setAttribute('content', currentTheme === 'signature' ? '#21080f' : (resolvedTheme === 'dark' ? '#09090b' : '#f2f2f4')); } updateThemeButtons(); sidebarThemeCurrent = document.querySelector('[data-lm-theme-current]'); if (sidebarThemeCurrent) { sidebarThemeCurrent.textContent = sidebarThemeCurrent.getAttribute('data-' + currentTheme) || currentTheme; } if (shouldStore) { writeStorage(themeStorageKey, currentTheme); } } function setButtonDisabled(button, isDisabled) { if (!button) { return; } button.disabled = isDisabled; button.setAttribute('aria-disabled', isDisabled ? 'true' : 'false'); if (isDisabled) { button.setAttribute('disabled', 'disabled'); } else { button.removeAttribute('disabled'); } } function updateFontButtons() { var scaleIndex = getFontScaleIndex(currentFontScale), sidebarFontCurrent = document.querySelector('[data-lm-font-current]'); setButtonDisabled(fontDecreaseButton, scaleIndex <= 0); setButtonDisabled(fontIncreaseButton, scaleIndex === fontScales.length - 1); if (fontScaleIndicator) { fontScaleIndicator.textContent = String(currentFontScale) + '%'; } if (sidebarFontCurrent) { sidebarFontCurrent.textContent = String(currentFontScale) + '%'; } } function applyFontScale(scale, shouldStore) { if (getFontScaleIndex(scale) === -1) { scale = 100; } currentFontScale = scale; root.setAttribute('data-lm-font-scale', String(currentFontScale)); if (root.style && typeof root.style.setProperty === 'function') { root.style.setProperty('--lm-font-scale', String(currentFontScale / 100)); } updateFontButtons(); if (typeof window.jQuery === 'function') { window.jQuery(window).triggerHandler('lm:font-scale-change'); } if (shouldStore) { writeStorage(fontStorageKey, String(currentFontScale)); } } function changeFontScale(direction) { var currentIndex = getFontScaleIndex(currentFontScale), nextIndex = currentIndex + direction; if (nextIndex >= 0 && nextIndex < fontScales.length) { applyFontScale(fontScales[nextIndex], true); } } function preventDefault(event) { event = event || window.event; if (event.preventDefault) { event.preventDefault(); } else { event.returnValue = false; } } function addEventListener(element, eventName, handler, useCapture) { if (!element) { return; } if (element.addEventListener) { element.addEventListener(eventName, handler, !!useCapture); } else if (element.attachEvent) { element.attachEvent('on' + eventName, function (event) { handler.call(element, event || window.event); }); } } function addClickListener(element, handler) { addEventListener(element, 'click', handler); } function bindSidebarRail() { var toggles = document.querySelectorAll('[data-lm-sidebar-toggle]'), desktopQuery = getMediaQuery('(min-width: 992px)'), storedState = readStorage(sidebarStorageKey), index; if (!toggles.length) { return; } function sidebarIsCollapsed() { return root.getAttribute('data-lm-sidebar') === 'collapsed'; } function syncToggle(toggle, collapsed) { var actionLabel = toggle.getAttribute(collapsed ? 'data-expand-label' : 'data-collapse-label'), srLabel = toggle.querySelector('[data-lm-sidebar-toggle-label]'); toggle.setAttribute('aria-expanded', collapsed ? 'false' : 'true'); if (actionLabel) { toggle.setAttribute('aria-label', actionLabel); toggle.setAttribute('title', actionLabel); toggle.setAttribute('data-lm-tooltip', actionLabel); if (srLabel) { srLabel.textContent = actionLabel; } } } function closeSidebarMenus() { var openMenus = document.querySelectorAll('.lm-navbar .open'), menuTriggers, menuIndex, openIndex; for (openIndex = 0; openIndex < openMenus.length; openIndex += 1) { openMenus[openIndex].classList.remove('open'); menuTriggers = openMenus[openIndex].querySelectorAll('[aria-expanded="true"]'); for (menuIndex = 0; menuIndex < menuTriggers.length; menuIndex += 1) { menuTriggers[menuIndex].setAttribute('aria-expanded', 'false'); } } } function applySidebarState(collapsed, shouldStore) { if (collapsed) { root.setAttribute('data-lm-sidebar', 'collapsed'); } else { root.removeAttribute('data-lm-sidebar'); } for (index = 0; index < toggles.length; index += 1) { syncToggle(toggles[index], collapsed); } if (shouldStore) { writeStorage(sidebarStorageKey, collapsed ? 'collapsed' : 'expanded'); } closeSidebarMenus(); } for (index = 0; index < toggles.length; index += 1) { addClickListener(toggles[index], function (event) { if (desktopQuery && !desktopQuery.matches) { return; } preventDefault(event); applySidebarState(!sidebarIsCollapsed(), true); }); } applySidebarState(storedState === 'collapsed', false); if (desktopQuery) { addEventListener(desktopQuery, 'change', function () { applySidebarState(readStorage(sidebarStorageKey) === 'collapsed', false); }); if (desktopQuery.addListener && !desktopQuery.addEventListener) { desktopQuery.addListener(function () { applySidebarState(readStorage(sidebarStorageKey) === 'collapsed', false); }); } } } function bindControls() { var index; themeButtons = document.querySelectorAll('.lm-theme-option[data-lm-theme]'); fontDecreaseButton = document.getElementById('lm-font-decrease'); fontResetButton = document.getElementById('lm-font-reset'); fontIncreaseButton = document.getElementById('lm-font-increase'); fontScaleIndicator = document.getElementById('lm-font-scale-indicator'); for (index = 0; index < themeButtons.length; index += 1) { addClickListener(themeButtons[index], function (event) { preventDefault(event); applyTheme(this.getAttribute('data-lm-theme'), true); }); } addClickListener(fontDecreaseButton, function (event) { preventDefault(event); changeFontScale(-1); }); addClickListener(fontResetButton, function (event) { preventDefault(event); applyFontScale(100, true); }); addClickListener(fontIncreaseButton, function (event) { preventDefault(event); changeFontScale(1); }); applyTheme(currentTheme, false); applyFontScale(currentFontScale, false); } function bindFontMenu() { var control = document.querySelector('.lm-font-control'), trigger, menu, choiceButtons, closeTimer, classObserver, index; if (!control) { return; } trigger = control.querySelector('.lm-font-trigger'); menu = control.querySelector('.lm-font-menu'); choiceButtons = control.querySelectorAll('.lm-font-button'); function syncExpandedState() { if (trigger) { trigger.setAttribute('aria-expanded', control.classList.contains('open') ? 'true' : 'false'); } } function openMenu() { window.clearTimeout(closeTimer); if (!control.classList.contains('open')) { control.classList.add('open'); restartMotionClass(control, 'lm-font-opening', 620); } syncExpandedState(); } function closeMenu() { window.clearTimeout(closeTimer); control.classList.remove('open'); syncExpandedState(); } function closeMenuSoon(ignoreFocus) { window.clearTimeout(closeTimer); closeTimer = window.setTimeout(function () { if (!control.matches(':hover') && (ignoreFocus || !control.contains(document.activeElement))) { closeMenu(); } }, 170); } if (finePointerQuery && finePointerQuery.matches) { addEventListener(control, 'mouseenter', openMenu); addEventListener(control, 'mouseleave', function () { closeMenuSoon(true); }); } addEventListener(control, 'focusin', function () { window.clearTimeout(closeTimer); }); addEventListener(control, 'focusout', function () { closeMenuSoon(false); }); addClickListener(trigger, function (event) { preventDefault(event); if (finePointerQuery && finePointerQuery.matches && event.detail > 0) { openMenu(); } else if (control.classList.contains('open')) { closeMenu(); } else { openMenu(); } }); addEventListener(document, 'click', function (event) { var target = event.target || event.srcElement; if (target && !control.contains(target)) { closeMenu(); } }); addEventListener(control, 'keydown', function (event) { if (event.key !== 'Escape' && event.keyCode !== 27) { return; } closeMenu(); if (trigger && typeof trigger.focus === 'function') { trigger.focus(); } }, true); for (index = 0; index < choiceButtons.length; index += 1) { addEventListener(choiceButtons[index], 'pointerdown', function () { restartMotionClass(this, 'lm-font-choice-committed', 560); restartMotionClass(control, 'lm-font-scale-changing', 680); }, true); addEventListener(choiceButtons[index], 'keydown', function (event) { if (event.key === 'Enter' || event.keyCode === 13 || event.key === ' ') { restartMotionClass(this, 'lm-font-choice-committed', 560); restartMotionClass(control, 'lm-font-scale-changing', 680); } }); } if (typeof MutationObserver === 'function') { classObserver = new MutationObserver(syncExpandedState); classObserver.observe(control, { attributes: true, attributeFilter: ['class'] }); } } function bindHeaderDisclosure(controlSelector, triggerSelector, itemSelector, openingClass, changingClass) { var control = document.querySelector(controlSelector), trigger, panel, items, closeTimer, classObserver, index; if (!control) { return; } trigger = control.querySelector(triggerSelector); panel = control.querySelector('.dropdown-menu'); items = control.querySelectorAll(itemSelector); if (!trigger || !panel) { return; } function syncExpandedState() { trigger.setAttribute('aria-expanded', control.classList.contains('open') ? 'true' : 'false'); } function openMenu() { window.clearTimeout(closeTimer); if (!control.classList.contains('open')) { control.classList.add('open'); restartMotionClass(control, openingClass, 660); } syncExpandedState(); } function closeMenu() { window.clearTimeout(closeTimer); control.classList.remove('open'); syncExpandedState(); } function closeMenuSoon(ignoreFocus) { window.clearTimeout(closeTimer); closeTimer = window.setTimeout(function () { if (!control.matches(':hover') && (ignoreFocus || !control.contains(document.activeElement))) { closeMenu(); } }, 170); } if (finePointerQuery && finePointerQuery.matches) { addEventListener(control, 'mouseenter', openMenu); addEventListener(control, 'mouseleave', function () { closeMenuSoon(true); }); } addEventListener(control, 'focusin', function () { window.clearTimeout(closeTimer); }); addEventListener(control, 'focusout', function () { closeMenuSoon(false); }); addClickListener(trigger, function (event) { preventDefault(event); if (finePointerQuery && finePointerQuery.matches && event.detail > 0) { openMenu(); } else if (control.classList.contains('open')) { closeMenu(); } else { openMenu(); } }); addEventListener(document, 'click', function (event) { var target = event.target || event.srcElement; if (target && !control.contains(target)) { closeMenu(); } }); addEventListener(control, 'keydown', function (event) { if (event.key !== 'Escape' && event.keyCode !== 27) { return; } closeMenu(); if (typeof trigger.focus === 'function') { trigger.focus(); } }, true); for (index = 0; index < items.length; index += 1) { addEventListener(items[index], 'pointerdown', function () { restartMotionClass(this, 'lm-utility-choice-committed', 560); restartMotionClass(control, changingClass, 700); }, true); addEventListener(items[index], 'keydown', function (event) { if (event.key === 'Enter' || event.keyCode === 13 || event.key === ' ') { restartMotionClass(this, 'lm-utility-choice-committed', 560); restartMotionClass(control, changingClass, 700); } }); } if (typeof MutationObserver === 'function') { classObserver = new MutationObserver(syncExpandedState); classObserver.observe(control, { attributes: true, attributeFilter: ['class'] }); } } function bindHeaderUtilityMenus() { bindHeaderDisclosure('.lm-language-control', '.lm-language-trigger', '.lm-language-option', 'lm-language-opening', 'lm-language-changing'); bindHeaderDisclosure('.lm-theme-control', '.lm-theme-trigger', '.lm-theme-option', 'lm-theme-opening', 'lm-theme-changing'); bindHeaderDisclosure('.lm-account-control', '.lm-account-trigger', '.lm-account-option', 'lm-account-opening', 'lm-account-changing'); } function bindBalanceWallet() { var menu = document.querySelector('.lm-balance-menu'), wallet, toggle, currencyLinks, closeTimer, classObserver, index; if (!menu) { return; } wallet = menu.querySelector('.lm-wallet-link'); toggle = menu.querySelector('.lm-currency-trigger'); currencyLinks = menu.querySelectorAll('#currencies-item'); function syncExpandedState() { if (toggle) { toggle.setAttribute('aria-expanded', menu.classList.contains('open') ? 'true' : 'false'); } } function openMenu() { window.clearTimeout(closeTimer); menu.classList.add('open'); syncExpandedState(); } function closeMenu() { window.clearTimeout(closeTimer); menu.classList.remove('open'); syncExpandedState(); } function closeMenuSoon(ignoreFocus) { window.clearTimeout(closeTimer); closeTimer = window.setTimeout(function () { if (!menu.matches(':hover') && (ignoreFocus || !menu.contains(document.activeElement))) { closeMenu(); } }, 150); } if (finePointerQuery && finePointerQuery.matches) { addEventListener(menu, 'mouseenter', openMenu); addEventListener(menu, 'mouseleave', function () { closeMenuSoon(true); }); } addEventListener(menu, 'focusin', function () { window.clearTimeout(closeTimer); }); addEventListener(menu, 'focusout', function () { closeMenuSoon(false); }); addClickListener(toggle, function (event) { preventDefault(event); if (finePointerQuery && finePointerQuery.matches && event.detail > 0) { openMenu(); } else if (menu.classList.contains('open')) { closeMenu(); } else { openMenu(); } }); addEventListener(document, 'click', function (event) { var target = event.target || event.srcElement; if (target && !menu.contains(target)) { closeMenu(); } }); addEventListener(menu, 'keydown', function (event) { if (event.key !== 'Escape' && event.keyCode !== 27) { return; } closeMenu(); if (toggle && typeof toggle.focus === 'function') { toggle.focus(); } }, true); addEventListener(wallet, 'pointerdown', function () { restartMotionClass(wallet, 'lm-wallet-activated', 640); }, true); addEventListener(wallet, 'keydown', function (event) { if (event.key === 'Enter' || event.keyCode === 13) { restartMotionClass(wallet, 'lm-wallet-activated', 640); } }); for (index = 0; index < currencyLinks.length; index += 1) { addEventListener(currencyLinks[index], 'pointerdown', function () { restartMotionClass(this, 'lm-currency-committed', 620); restartMotionClass(menu, 'lm-currency-switching', 760); }, true); addEventListener(currencyLinks[index], 'keydown', function (event) { if (event.key === 'Enter' || event.keyCode === 13) { restartMotionClass(this, 'lm-currency-committed', 620); restartMotionClass(menu, 'lm-currency-switching', 760); } }); } if (typeof MutationObserver === 'function') { classObserver = new MutationObserver(syncExpandedState); classObserver.observe(menu, { attributes: true, attributeFilter: ['class'] }); } } function bindTicketAlertDismiss() { var closeButtons = document.querySelectorAll('.ticket-danger .close'), index; for (index = 0; index < closeButtons.length; index += 1) { addClickListener(closeButtons[index], function (event) { var alertElement = this.parentNode, messageElement; preventDefault(event); if (!alertElement) { return; } alertElement.style.display = 'none'; messageElement = alertElement.querySelector('div'); if (messageElement) { messageElement.textContent = ''; } }); } } function handleSystemThemeChange() { if (currentTheme === 'system') { applyTheme('system', false); } } function bindSystemThemeListener() { if (!systemThemeQuery) { return; } if (typeof systemThemeQuery.addEventListener === 'function') { systemThemeQuery.addEventListener('change', handleSystemThemeChange); } else if (typeof systemThemeQuery.addListener === 'function') { systemThemeQuery.addListener(handleSystemThemeChange); } } function getScrollTop() { if (typeof window.pageYOffset === 'number') { return window.pageYOffset; } return (document.documentElement && document.documentElement.scrollTop) || (document.body && document.body.scrollTop) || 0; } function updateScrollState() { scrollFrameScheduled = false; root.setAttribute('data-lm-scrolled', getScrollTop() > 12 ? 'true' : 'false'); } function scheduleScrollStateUpdate() { if (scrollFrameScheduled) { return; } scrollFrameScheduled = true; requestFrame(updateScrollState); } function bindScrollState() { updateScrollState(); addEventListener(window, 'scroll', scheduleScrollStateUpdate); } function pointerEffectsAllowed() { return Boolean( finePointerQuery && finePointerQuery.matches && !(reducedMotionQuery && reducedMotionQuery.matches) ); } function flushPointerPosition() { var surface = pendingPointerSurface, rect, x, y; pointerFrameScheduled = false; if (!surface || !pointerEffectsAllowed() || !surface.getBoundingClientRect) { return; } rect = surface.getBoundingClientRect(); x = Math.max(0, Math.min(rect.width || rect.right - rect.left, pendingPointerX - rect.left)); y = Math.max(0, Math.min(rect.height || rect.bottom - rect.top, pendingPointerY - rect.top)); if (surface.style && typeof surface.style.setProperty === 'function') { surface.style.setProperty('--lm-pointer-x', Math.round(x) + 'px'); surface.style.setProperty('--lm-pointer-y', Math.round(y) + 'px'); } } function handlePointerMove(event) { event = event || window.event; if (!pointerEffectsAllowed() || typeof event.clientX !== 'number' || typeof event.clientY !== 'number') { return; } pendingPointerSurface = this; pendingPointerX = event.clientX; pendingPointerY = event.clientY; if (!pointerFrameScheduled) { pointerFrameScheduled = true; requestFrame(flushPointerPosition); } } function handlePointerLeave() { if (pendingPointerSurface === this) { pendingPointerSurface = null; } if (this.style && typeof this.style.removeProperty === 'function') { this.style.removeProperty('--lm-pointer-x'); this.style.removeProperty('--lm-pointer-y'); } } function bindPointerEffects() { var surfaces, index; if (!pointerEffectsAllowed()) { return; } surfaces = document.querySelectorAll('.lm-interactive-surface'); for (index = 0; index < surfaces.length; index += 1) { addEventListener(surfaces[index], 'mousemove', handlePointerMove); addEventListener(surfaces[index], 'mouseleave', handlePointerLeave); } } function liveRoomMotionAllowed() { return !(reducedMotionQuery && reducedMotionQuery.matches) && !(typeof document.hidden === 'boolean' && document.hidden); } function setLiveRoomMotion(room, useMotion) { var trailStage = room.querySelector('.lm-live-room__trails'); root.removeAttribute('data-lm-fluid-state'); root.setAttribute('data-lm-live-room-state', useMotion ? 'motion' : 'still'); if (!trailStage) { return; } if (useMotion && typeof trailStage.unpauseAnimations === 'function') { trailStage.unpauseAnimations(); } else if (!useMotion && typeof trailStage.pauseAnimations === 'function') { trailStage.pauseAnimations(); } } function bindLiveRoomAtmosphere() { var room = document.querySelector('[data-lm-live-room]'), comments = [ '老板,我要下单!', 'Boss, I’m ready to order!', '吉隆坡的朋友来了!', 'KL crew is in the room!', '这个效果太稳了!', 'This looks super smooth!', '价钱可以,直接来!', 'Good price—let’s go!', '槟城也可以安排吗?', 'Can you do Penang too?', '刚进来就被吸引了!', 'I just joined—love this!', '柔佛的朋友在哪里?', 'Johor crew, where are you?', '老板,发一下链接!', 'Boss, drop the link!', '我朋友也想要这个!', 'My friend wants this too!', '今天的直播太精彩了!', 'This live is amazing!' ], commentBubbles, commentCycleIndex = 0, commentCycleTimer = null, clearCommentTimers = function () { var index; if (commentCycleTimer) { window.clearTimeout(commentCycleTimer); commentCycleTimer = null; } for (index = 0; index < commentBubbles.length; index += 1) { if (commentBubbles[index]._lmCommentTypingTimer) { window.clearTimeout(commentBubbles[index]._lmCommentTypingTimer); commentBubbles[index]._lmCommentTypingTimer = null; } } }, typeComment = function (bubble, message, useMotion) { var target = bubble.querySelector('.lm-live-room__comment-text'), position = 0, typeNextCharacter; if (!target) { return; } bubble.setAttribute('data-lm-comment-state', useMotion ? 'typing' : 'complete'); if (!useMotion) { target.textContent = message; return; } target.textContent = ''; typeNextCharacter = function () { position += 1; target.textContent = message.substring(0, position); if (position < message.length && liveRoomMotionAllowed()) { bubble._lmCommentTypingTimer = window.setTimeout(typeNextCharacter, 42 + ((position % 3) * 8)); } else { bubble._lmCommentTypingTimer = null; target.textContent = message; bubble.setAttribute('data-lm-comment-state', 'complete'); } }; bubble._lmCommentTypingTimer = window.setTimeout(typeNextCharacter, 180); }, renderComments = function (useMotion) { var index, offset; clearCommentTimers(); for (index = 0; index < commentBubbles.length; index += 1) { offset = parseInt(commentBubbles[index].getAttribute('data-lm-comment-offset'), 10) || 0; typeComment(commentBubbles[index], comments[(commentCycleIndex + offset) % comments.length], useMotion); } if (useMotion) { commentCycleTimer = window.setTimeout(function () { commentCycleIndex = (commentCycleIndex + 1) % comments.length; renderComments(liveRoomMotionAllowed()); }, 4000); } }, refresh = function () { if (room) { var useMotion = liveRoomMotionAllowed(); setLiveRoomMotion(room, useMotion); renderComments(useMotion); } }; if (!room) { return; } commentBubbles = room.querySelectorAll('[data-lm-live-comment]'); addEventListener(document, 'visibilitychange', refresh); refresh(); if (reducedMotionQuery) { if (typeof reducedMotionQuery.addEventListener === 'function') { reducedMotionQuery.addEventListener('change', refresh); } else if (typeof reducedMotionQuery.addListener === 'function') { reducedMotionQuery.addListener(refresh); } } } function createQuantityAssistElement(tagName, className, text) { var element = document.createElement(tagName); if (className) { element.className = className; } if (typeof text === 'string') { element.textContent = text; } return element; } function getQuantityAssistCopy(fields, name, fallback) { return fields.getAttribute('data-lm-quantity-' + name) || fallback; } function formatQuantityPreset(value) { return String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ','); } function getQuantityRange(input) { var group = input.parentNode, help, matches, values = [], directMin = parseInt(input.getAttribute('min'), 10), directMax = parseInt(input.getAttribute('max'), 10), index, normalized; while (group && group !== document.body && !((' ' + group.className + ' ').indexOf(' form-group ') !== -1)) { group = group.parentNode; } help = group && group.querySelector ? group.querySelector('.min-max, .help-block') : null; matches = help ? help.textContent.match(/\d[\d\s.,\u00a0\u202f]*/g) : null; if (matches) { for (index = 0; index < matches.length; index += 1) { normalized = matches[index].replace(/\D/g, ''); if (normalized) { values.push(parseInt(normalized, 10)); } } } return { min: isNaN(directMin) ? (values.length ? values[0] : null) : directMin, max: isNaN(directMax) ? (values.length > 1 ? values[1] : null) : directMax }; } function dispatchQuantityEvent(input, eventName) { var event; if (document.createEvent) { event = document.createEvent('HTMLEvents'); event.initEvent(eventName, true, false); input.dispatchEvent(event); } else if (input.fireEvent) { input.fireEvent('on' + eventName); } } function setQuantityInputValue(input, value) { input.value = String(value); dispatchQuantityEvent(input, 'input'); dispatchQuantityEvent(input, 'change'); if (window.jQuery) { window.jQuery(input).trigger('keyup'); } if (typeof input.focus === 'function') { input.focus(); } } function refreshQuantityPresetStates(assist, input) { var buttons = assist.querySelectorAll('[data-lm-quantity-value]'), range = getQuantityRange(input), currentValue = parseInt(String(input.value || '').replace(/\D/g, ''), 10), unavailableCopy = assist.getAttribute('data-lm-unavailable-copy') || '', index, button, value, unavailable, selected, selectedStage = 0; for (index = 0; index < buttons.length; index += 1) { button = buttons[index]; value = parseInt(button.getAttribute('data-lm-quantity-value'), 10); unavailable = (range.min !== null && value < range.min) || (range.max !== null && value > range.max); selected = !isNaN(currentValue) && currentValue === value; button.disabled = unavailable; button.setAttribute('aria-disabled', unavailable ? 'true' : 'false'); button.setAttribute('aria-pressed', selected ? 'true' : 'false'); if (unavailable && unavailableCopy) { button.setAttribute('title', unavailableCopy); } else { button.removeAttribute('title'); } if (selected) { button.classList.add('is-selected'); selectedStage = parseInt(button.getAttribute('data-lm-quantity-stage-index'), 10) || 0; } else { button.classList.remove('is-selected'); } } if (selectedStage > 0) { revealQuantityStage(assist, selectedStage); } refreshQuantityExpandStates(assist); } function refreshQuantityExpandStates(assist) { var expandButtons = assist.querySelectorAll('[data-lm-quantity-action="expand"]'), unavailableCopy = assist.getAttribute('data-lm-unavailable-copy') || '', index, candidateIndex, expandButton, targetStage, candidates, hasAvailable; for (index = 0; index < expandButtons.length; index += 1) { expandButton = expandButtons[index]; targetStage = parseInt(expandButton.getAttribute('data-lm-quantity-target-stage'), 10); candidates = assist.querySelectorAll('[data-lm-quantity-stage-index]'); hasAvailable = false; for (candidateIndex = 0; candidateIndex < candidates.length; candidateIndex += 1) { if (parseInt(candidates[candidateIndex].getAttribute('data-lm-quantity-stage-index'), 10) >= targetStage && !candidates[candidateIndex].disabled) { hasAvailable = true; break; } } expandButton.disabled = !hasAvailable; expandButton.setAttribute('aria-disabled', hasAvailable ? 'false' : 'true'); if (!hasAvailable && unavailableCopy) { expandButton.setAttribute('title', unavailableCopy); } else { expandButton.removeAttribute('title'); } } } function revealQuantityStage(assist, stageIndex) { var index, stage, previousExpand, nextExpand, nextStage; for (index = 1; index <= stageIndex; index += 1) { stage = assist.querySelector('[data-lm-quantity-stage="' + index + '"]'); previousExpand = assist.querySelector('[data-lm-quantity-target-stage="' + index + '"]'); if (stage) { stage.hidden = false; } if (previousExpand) { previousExpand.hidden = true; previousExpand.setAttribute('aria-expanded', 'true'); } } nextStage = assist.querySelector('[data-lm-quantity-stage="' + (stageIndex + 1) + '"]'); nextExpand = assist.querySelector('[data-lm-quantity-target-stage="' + (stageIndex + 1) + '"]'); if (nextExpand && nextStage && nextStage.hidden) { nextExpand.hidden = false; } } function buildQuantityPresetAssist(fields, input) { var group = input.parentNode, assist, heading, headingLabel, progressive, stage, stageValues = [ [50, 100, 200], [300, 500, 1000], [2000, 5000, 10000, 50000] ], expandButton, expandLabel, moreCopy = getQuantityAssistCopy(fields, 'more', 'Show more quantities'), liveStatus, stageIndex, index, button, clickTarget, action, value, targetStage, selectedCopy = getQuantityAssistCopy(fields, 'selected', 'Selected'); while (group && group !== document.body && !((' ' + group.className + ' ').indexOf(' form-group ') !== -1)) { group = group.parentNode; } if (!group || !group.querySelector) { return null; } assist = group.querySelector('.lm-quantity-assist'); if (assist) { refreshQuantityPresetStates(assist, input); return assist; } assist = createQuantityAssistElement('div', 'lm-quantity-assist'); assist.setAttribute('data-lm-unavailable-copy', getQuantityAssistCopy(fields, 'unavailable', 'Outside the current service range')); heading = createQuantityAssistElement('div', 'lm-quantity-assist__heading'); headingLabel = createQuantityAssistElement('span', 'lm-quantity-assist__label', getQuantityAssistCopy(fields, 'quick', 'Quick quantity')); heading.appendChild(headingLabel); progressive = createQuantityAssistElement('div', 'lm-quantity-progressive'); progressive.setAttribute('role', 'group'); progressive.setAttribute('aria-label', headingLabel.textContent); for (stageIndex = 0; stageIndex < stageValues.length; stageIndex += 1) { stage = createQuantityAssistElement('div', 'lm-quantity-stage lm-quantity-stage--' + stageIndex); stage.id = 'lm-quantity-stage-' + stageIndex; stage.setAttribute('data-lm-quantity-stage', String(stageIndex)); stage.hidden = stageIndex > 0; for (index = 0; index < stageValues[stageIndex].length; index += 1) { button = createQuantityAssistElement('button', 'lm-quantity-preset', formatQuantityPreset(stageValues[stageIndex][index])); button.type = 'button'; button.setAttribute('data-lm-quantity-action', 'select'); button.setAttribute('data-lm-quantity-value', String(stageValues[stageIndex][index])); button.setAttribute('data-lm-quantity-stage-index', String(stageIndex)); button.setAttribute('aria-pressed', 'false'); stage.appendChild(button); } progressive.appendChild(stage); if (stageIndex < stageValues.length - 1) { expandButton = createQuantityAssistElement('button', 'lm-quantity-expand'); expandButton.type = 'button'; expandButton.hidden = stageIndex > 0; expandButton.setAttribute('data-lm-quantity-action', 'expand'); expandButton.setAttribute('data-lm-quantity-target-stage', String(stageIndex + 1)); expandButton.setAttribute('aria-expanded', 'false'); expandButton.setAttribute('aria-controls', 'lm-quantity-stage-' + (stageIndex + 1)); expandLabel = moreCopy + ': ' + stageValues[stageIndex + 1].map(formatQuantityPreset).join(', '); expandButton.setAttribute('aria-label', expandLabel); expandButton.appendChild(createQuantityAssistElement('span', 'lm-quantity-expand__chevron')); progressive.appendChild(expandButton); } } liveStatus = createQuantityAssistElement('span', 'sr-only'); liveStatus.setAttribute('aria-live', 'polite'); assist.appendChild(heading); assist.appendChild(progressive); assist.appendChild(liveStatus); group.appendChild(assist); addEventListener(input, 'input', function () { refreshQuantityPresetStates(assist, input); }); addEventListener(assist, 'click', function (event) { clickTarget = event.target || event.srcElement; while (clickTarget && clickTarget !== assist && clickTarget.tagName !== 'BUTTON') { clickTarget = clickTarget.parentNode; } if (!clickTarget || clickTarget === assist || clickTarget.disabled) { return; } action = clickTarget.getAttribute('data-lm-quantity-action'); if (action === 'select') { value = parseInt(clickTarget.getAttribute('data-lm-quantity-value'), 10); setQuantityInputValue(input, value); liveStatus.textContent = selectedCopy + ' ' + formatQuantityPreset(value); refreshQuantityPresetStates(assist, input); } else if (action === 'expand') { targetStage = parseInt(clickTarget.getAttribute('data-lm-quantity-target-stage'), 10); revealQuantityStage(assist, targetStage); clickTarget.setAttribute('aria-expanded', 'true'); liveStatus.textContent = moreCopy + ': ' + stageValues[targetStage].map(formatQuantityPreset).join(', '); } }); input.setAttribute('data-lm-quantity-enhanced', 'true'); refreshQuantityPresetStates(assist, input); return assist; } function bindQuantityPresetAssist() { var fields = document.getElementById('fields'), observer, refreshScheduled = false, refresh = function () { var input; refreshScheduled = false; if (!fields || !fields.querySelector) { return; } input = fields.querySelector('#orderform-quantity, input[name="OrderForm[quantity]"]'); if (input) { buildQuantityPresetAssist(fields, input); } }, scheduleRefresh = function () { if (refreshScheduled) { return; } refreshScheduled = true; requestFrame(refresh); }; if (!fields) { return; } refresh(); if (typeof MutationObserver === 'function') { observer = new MutationObserver(scheduleRefresh); observer.observe(fields, { childList: true, subtree: true, characterData: true }); } addEventListener(document.getElementById('orderform-service'), 'change', scheduleRefresh); } function bindChargeCurrencyBoard() { var board = document.querySelector('[data-lm-charge-board]'), chargeInput = document.getElementById('charge'), ratesContainer, rateItems, primaryCode, liveRegion, loadingCopy, unavailableCopy, activeCode = '', activeItem = null, activeHeaderLink, exchangeRates = null, lastChargeValue = null, rateObserver, monitorTimer, index; if (!board || !chargeInput || board.getAttribute('data-lm-charge-bound') === 'true') { return; } board.setAttribute('data-lm-charge-bound', 'true'); ratesContainer = board.querySelector('[data-lm-charge-rates]'); primaryCode = board.querySelector('[data-lm-charge-primary-code]'); liveRegion = board.querySelector('[data-lm-charge-live]'); loadingCopy = board.getAttribute('data-lm-charge-loading') || 'Calculating'; unavailableCopy = board.getAttribute('data-lm-charge-unavailable') || 'Conversion unavailable'; if (!ratesContainer) { return; } rateItems = Array.prototype.slice.call(ratesContainer.querySelectorAll('[data-lm-charge-rate]')); for (index = 0; index < rateItems.length; index += 1) { if (rateItems[index].getAttribute('data-lm-currency-active') === 'true') { activeItem = rateItems[index]; activeCode = rateItems[index].getAttribute('data-lm-currency-code') || ''; break; } } if (!activeCode) { activeHeaderLink = document.querySelector('#currencies-list li.active a[data-rate-key]'); activeCode = activeHeaderLink ? activeHeaderLink.getAttribute('data-rate-key') : ''; } if (!activeCode && rateItems.length) { activeCode = rateItems[0].getAttribute('data-lm-currency-code') || ''; } activeCode = String(activeCode || '').toUpperCase(); board.setAttribute('data-lm-active-currency', activeCode); if (primaryCode) { primaryCode.textContent = activeCode; } function getSecondaryPriority(code) { var normalized = String(code || '').toUpperCase(), preferredCore = activeCode === 'MYR' ? 'USD' : 'MYR', remainingOrder = ['USD', 'MYR', 'CNY', 'TWD'], remainingIndex; if (normalized === activeCode) { return -1; } if (normalized === preferredCore) { return 0; } if (activeCode !== 'MYR' && activeCode !== 'USD' && normalized === 'USD') { return 1; } remainingIndex = remainingOrder.indexOf(normalized); return remainingIndex === -1 ? 100 : 10 + remainingIndex; } rateItems.sort(function (first, second) { var firstCode = first.getAttribute('data-lm-currency-code'), secondCode = second.getAttribute('data-lm-currency-code'); return getSecondaryPriority(firstCode) - getSecondaryPriority(secondCode); }); var visibleRateIndex = 0; for (index = 0; index < rateItems.length; index += 1) { var itemCode = String(rateItems[index].getAttribute('data-lm-currency-code') || '').toUpperCase(), isActive = itemCode === activeCode; rateItems[index].hidden = isActive; rateItems[index].setAttribute('aria-hidden', isActive ? 'true' : 'false'); rateItems[index].classList.remove('lm-charge-rate--core'); if (!isActive) { rateItems[index].style.setProperty('--lm-rate-order', String(visibleRateIndex)); if (visibleRateIndex === 0) { rateItems[index].classList.add('lm-charge-rate--core'); } visibleRateIndex += 1; } ratesContainer.appendChild(rateItems[index]); } function parseChargeAmount(value) { var normalized = String(value || '').replace(/\s/g, '').replace(/[^0-9,.-]/g, ''), lastComma, lastDot, decimalMark, parts, number; if (!normalized) { return null; } lastComma = normalized.lastIndexOf(','); lastDot = normalized.lastIndexOf('.'); if (lastComma !== -1 && lastDot !== -1) { decimalMark = lastComma > lastDot ? ',' : '.'; normalized = normalized.replace(decimalMark === ',' ? /\./g : /,/g, ''); normalized = normalized.replace(decimalMark, '.'); } else if (lastComma !== -1) { parts = normalized.split(','); normalized = parts.length === 2 && parts[1].length <= 3 ? parts[0] + '.' + parts[1] : parts.join(''); } else if (lastDot !== -1) { parts = normalized.split('.'); normalized = parts.length === 2 && parts[1].length <= 3 ? normalized : parts.join(''); } number = parseFloat(normalized); return isFinite(number) ? number : null; } function formatReferenceAmount(code, symbol, value) { var decimals = Math.abs(value) > 0 && Math.abs(value) < 1 ? 3 : 2, formatted; if (typeof Intl !== 'undefined' && Intl.NumberFormat) { formatted = new Intl.NumberFormat(document.documentElement.lang || undefined, { minimumFractionDigits: decimals, maximumFractionDigits: decimals }).format(value); } else { formatted = value.toFixed(decimals); } return (symbol ? symbol + ' ' : code + ' ') + formatted; } function renderReferenceAmounts(force) { var rawCharge = chargeInput.value || '', chargeAmount, valueElement, currencyCode, currencySymbol, convertedValue, hasRenderedValue = false; if (!force && rawCharge === lastChargeValue) { return; } lastChargeValue = rawCharge; chargeAmount = parseChargeAmount(rawCharge); board.classList.toggle('lm-charge-board--empty', chargeAmount === null); for (index = 0; index < rateItems.length; index += 1) { if (rateItems[index] === activeItem || rateItems[index].hidden) { continue; } valueElement = rateItems[index].querySelector('[data-lm-charge-rate-value]'); currencyCode = String(rateItems[index].getAttribute('data-lm-currency-code') || '').toUpperCase(); currencySymbol = rateItems[index].getAttribute('data-lm-currency-symbol') || ''; if (!valueElement) { continue; } if (chargeAmount === null) { valueElement.textContent = '—'; } else if (!exchangeRates) { valueElement.textContent = loadingCopy; } else if (typeof exchangeRates[currencyCode] === 'number') { convertedValue = chargeAmount * exchangeRates[currencyCode]; valueElement.textContent = formatReferenceAmount(currencyCode, currencySymbol, convertedValue); hasRenderedValue = true; } else { valueElement.textContent = unavailableCopy; } } if (hasRenderedValue) { restartMotionClass(board, 'lm-charge-board--updated', 620); } } function setRatesUnavailable() { var valueElement; board.classList.add('lm-charge-board--unavailable'); for (index = 0; index < rateItems.length; index += 1) { if (rateItems[index].hidden) { continue; } valueElement = rateItems[index].querySelector('[data-lm-charge-rate-value]'); if (valueElement) { valueElement.textContent = unavailableCopy; } } if (liveRegion) { liveRegion.textContent = unavailableCopy; } } function applyExchangeRates(rates) { if (!rates || typeof rates !== 'object') { return false; } exchangeRates = rates; board.classList.remove('lm-charge-board--loading', 'lm-charge-board--unavailable'); renderReferenceAmounts(true); return true; } function readCachedRates() { var cacheKey = 'lm-charge-rates-' + activeCode, cached; try { cached = JSON.parse(window.localStorage.getItem(cacheKey)); if (cached && cached.savedAt && (new Date().getTime() - cached.savedAt) < 86400000) { return cached.rates; } } catch (error) { return null; } return null; } function cacheRates(rates) { try { window.localStorage.setItem('lm-charge-rates-' + activeCode, JSON.stringify({ savedAt: new Date().getTime(), rates: rates })); } catch (error) { // Reference conversion still works without local caching. } } function requestExchangeRates() { var cachedRates = readCachedRates(); if (cachedRates) { applyExchangeRates(cachedRates); } else { board.classList.add('lm-charge-board--loading'); renderReferenceAmounts(true); } if (!activeCode || typeof window.fetch !== 'function') { if (!cachedRates) { setRatesUnavailable(); } return; } window.fetch('https://open.er-api.com/v6/latest/' + encodeURIComponent(activeCode), { method: 'GET', mode: 'cors', credentials: 'omit', cache: 'default', referrerPolicy: 'no-referrer' }).then(function (response) { if (!response.ok) { throw new Error('Reference rate request failed'); } return response.json(); }).then(function (payload) { if (!payload || payload.result !== 'success' || !payload.rates) { throw new Error('Reference rate response is invalid'); } cacheRates(payload.rates); applyExchangeRates(payload.rates); }).catch(function () { if (!cachedRates) { setRatesUnavailable(); } }); } function monitorChargeValue() { renderReferenceAmounts(false); monitorTimer = window.setTimeout(monitorChargeValue, document.hidden ? 1000 : 360); } addEventListener(chargeInput, 'input', function () { renderReferenceAmounts(true); }); addEventListener(chargeInput, 'change', function () { renderReferenceAmounts(true); }); if (typeof MutationObserver === 'function') { rateObserver = new MutationObserver(function () { renderReferenceAmounts(true); }); rateObserver.observe(chargeInput, { attributes: true, attributeFilter: ['value'] }); } requestExchangeRates(); monitorChargeValue(); } function closestElement(element, selector) { if (element && element.nodeType !== 1) { element = element.parentNode; } if (!element || typeof element.closest !== 'function') { return null; } return element.closest(selector); } function restartMotionClass(element, className, duration) { if (!element) { return; } element.classList.remove(className); void element.offsetWidth; element.classList.add(className); window.setTimeout(function () { element.classList.remove(className); }, duration); } function getSelect2Container(select) { var sibling; if (!select) { return null; } sibling = select.nextElementSibling; if (sibling && sibling.classList && sibling.classList.contains('select2')) { return sibling; } return select.parentNode ? select.parentNode.querySelector('.select2-container') : null; } function pulseOrderSelect(select) { var container = getSelect2Container(select); if (!container) { return; } restartMotionClass(container, 'lm-metal-confirm', 1080); } function pulseOrderSelectById(selectId) { pulseOrderSelect(document.getElementById(selectId)); } function bindNewOrderChoiceMotion() { var form = document.getElementById('order-form'), optionObserver, lastCategoryValue, lastServiceValue; if (!form || form.getAttribute('data-lm-choice-motion') === 'true') { return; } form.setAttribute('data-lm-choice-motion', 'true'); root.classList.add('lm-new-order-active'); lastCategoryValue = document.getElementById('orderform-category') ? document.getElementById('orderform-category').value : ''; lastServiceValue = document.getElementById('orderform-service') ? document.getElementById('orderform-service').value : ''; function armSearchConfirmation() { root.setAttribute('data-lm-search-armed-until', String(new Date().getTime() + 10000)); } function isSearchConfirmationArmed() { return parseInt(root.getAttribute('data-lm-search-armed-until'), 10) > new Date().getTime(); } function playSearchConfirmed() { restartMotionClass(document.getElementById('order-form'), 'lm-search-confirmed', 1040); root.removeAttribute('data-lm-search-armed-until'); } function confirmArmedSearch(delay) { if (!isSearchConfirmationArmed()) { return; } root.removeAttribute('data-lm-search-armed-until'); window.setTimeout(playSearchConfirmed, delay || 0); } function activateOption(option) { var searchGroup, options = option ? closestElement(option, '.select2-results__options') : null, optionsId = options ? (options.getAttribute('id') || '') : ''; if (!option || option.getAttribute('data-lm-choice-activating') === 'true') { return; } option.setAttribute('data-lm-choice-activating', 'true'); window.setTimeout(function () { option.removeAttribute('data-lm-choice-activating'); }, 520); searchGroup = closestElement(option, '.lm-service-search'); restartMotionClass(option, 'lm-option-committed', 720); if (searchGroup) { armSearchConfirmation(); window.setTimeout(function () { playSearchConfirmed(); }, 240); window.setTimeout(function () { pulseOrderSelectById('orderform-category'); }, 120); window.setTimeout(function () { pulseOrderSelectById('orderform-service'); }, 420); return; } if (optionsId.indexOf('select2-orderform-') === 0) { confirmArmedSearch(260); if (optionsId.indexOf('category') !== -1) { window.setTimeout(function () { pulseOrderSelectById('orderform-category'); }, 60); window.setTimeout(function () { pulseOrderSelectById('orderform-service'); }, 420); } else if (optionsId.indexOf('service') !== -1) { window.setTimeout(function () { pulseOrderSelectById('orderform-service'); }, 60); } } } function bindOption(option) { if (!option || option.getAttribute('data-lm-choice-bound') === 'true') { return; } option.setAttribute('data-lm-choice-bound', 'true'); addEventListener(option, 'pointerdown', function () { activateOption(option); }, true); addEventListener(option, 'mousedown', function () { activateOption(option); }, true); addEventListener(option, 'click', function () { activateOption(option); }, true); } function bindVisibleOptions(scope) { var options, index; if (!scope || typeof scope.querySelectorAll !== 'function') { return; } options = scope.querySelectorAll('.select2-results__option'); for (index = 0; index < options.length; index += 1) { bindOption(options[index]); } } bindVisibleOptions(document); if (typeof MutationObserver === 'function' && document.body) { optionObserver = new MutationObserver(function (mutations) { var index; for (index = 0; index < mutations.length; index += 1) { if (mutations[index].addedNodes && mutations[index].addedNodes.length) { bindVisibleOptions(mutations[index].target); } } }); optionObserver.observe(document.body, { childList: true, subtree: true }); } addEventListener(document, 'click', function (event) { var target = event.target || event.srcElement, option = closestElement(target, '.select2-results__option'), searchGroup = closestElement(target, '.lm-service-search'), inputWrapper = closestElement(target, '.lm-service-search .input-wrapper'); if (option) { activateOption(option); return; } if (searchGroup && inputWrapper) { armSearchConfirmation(); restartMotionClass(searchGroup, 'lm-search-burst', 920); } }, true); window.setInterval(function () { var currentCategory = document.getElementById('orderform-category'), currentService = document.getElementById('orderform-service'), categoryValue = currentCategory ? currentCategory.value : '', serviceValue = currentService ? currentService.value : '', categoryChanged = categoryValue !== lastCategoryValue, serviceChanged = serviceValue !== lastServiceValue; if (!categoryChanged && !serviceChanged) { return; } lastCategoryValue = categoryValue; lastServiceValue = serviceValue; confirmArmedSearch(260); if (categoryChanged) { pulseOrderSelectById('orderform-category'); window.setTimeout(function () { pulseOrderSelectById('orderform-service'); }, 360); } else if (serviceChanged) { pulseOrderSelectById('orderform-service'); } }, 140); addEventListener(document, 'change', function (event) { var target = event.target || event.srcElement; if (!target || (target.id !== 'orderform-category' && target.id !== 'orderform-service')) { return; } confirmArmedSearch(260); window.setTimeout(function () { pulseOrderSelectById(target.id); }, 30); if (target.id === 'orderform-category') { window.setTimeout(function () { pulseOrderSelectById('orderform-service'); }, 460); } }, true); } function bindNewOrderHoverSelects() { var form = document.getElementById('order-form'), openTimer, closeTimer, containerObserver, activeSelect = null, dropdownHovered = false; if (!form || form.getAttribute('data-lm-hover-selects') === 'true' || !finePointerQuery || !finePointerQuery.matches) { return; } form.setAttribute('data-lm-hover-selects', 'true'); function isOrderSelect(select) { return select && (select.id === 'orderform-category' || select.id === 'orderform-service'); } function getSelectFromContainer(container) { var select = container ? container.previousElementSibling : null; return isOrderSelect(select) ? select : null; } function getSelectApi(select) { var api = null; try { if (typeof $ === 'function') { api = $(select); } } catch (error) { api = null; } return api; } function triggerSelectionMouseDown(selection) { var mouseEvent; if (!selection) { return false; } if (document.createEvent) { mouseEvent = document.createEvent('MouseEvents'); mouseEvent.initMouseEvent('mousedown', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); selection.dispatchEvent(mouseEvent); return true; } if (typeof selection.click === 'function') { selection.click(); return true; } return false; } function decorateDropdown(select) { var results, dropdown; if (!isOrderSelect(select)) { return; } results = document.getElementById('select2-' + select.id + '-results'); dropdown = closestElement(results, '.select2-dropdown'); if (!dropdown) { return; } dropdown.classList.add('lm-order-choice-dropdown'); dropdown.setAttribute('data-lm-order-select', select.id === 'orderform-category' ? 'category' : 'service'); if (dropdown.getAttribute('data-lm-hover-bound') !== 'true') { dropdown.setAttribute('data-lm-hover-bound', 'true'); addEventListener(dropdown, 'mouseenter', function () { dropdownHovered = true; window.clearTimeout(closeTimer); }); addEventListener(dropdown, 'mouseleave', function () { dropdownHovered = false; scheduleClose(activeSelect); }); } } function openSelect(select) { var container = getSelect2Container(select), selection, api; window.clearTimeout(closeTimer); if (!container || container.classList.contains('select2-container--open')) { activeSelect = select; decorateDropdown(select); return; } if (activeSelect && activeSelect !== select) { closeSelect(activeSelect, true); } activeSelect = select; restartMotionClass(container, 'lm-select-hover-intent', 620); selection = container.querySelector('.select2-selection'); api = getSelectApi(select); if (!triggerSelectionMouseDown(selection) && api && typeof api.select2 === 'function') { api.select2('open'); } window.setTimeout(function () { decorateDropdown(select); }, 0); window.setTimeout(function () { decorateDropdown(select); }, 80); } function closeSelect(select, force) { var container = getSelect2Container(select), selection, api; if (!isOrderSelect(select) || !container || !container.classList.contains('select2-container--open')) { if (activeSelect === select) { activeSelect = null; } return; } if (!force && (dropdownHovered || container.matches(':hover'))) { return; } api = getSelectApi(select); selection = container.querySelector('.select2-selection'); if (!triggerSelectionMouseDown(selection) && api && typeof api.select2 === 'function') { api.select2('close'); } if (activeSelect === select) { activeSelect = null; } } function scheduleClose(select) { window.clearTimeout(closeTimer); closeTimer = window.setTimeout(function () { closeSelect(select || activeSelect, false); }, 230); } function bindSelectContainer(select) { var container = getSelect2Container(select); if (!isOrderSelect(select) || !container || container.getAttribute('data-lm-hover-bound') === 'true') { return; } container.setAttribute('data-lm-hover-bound', 'true'); addEventListener(container, 'mouseenter', function () { window.clearTimeout(openTimer); window.clearTimeout(closeTimer); openTimer = window.setTimeout(function () { if (container.matches(':hover')) { openSelect(select); } }, 140); }); addEventListener(container, 'mouseleave', function () { window.clearTimeout(openTimer); scheduleClose(select); }); addEventListener(container, 'pointerdown', function () { activeSelect = select; window.setTimeout(function () { decorateDropdown(select); }, 30); }, true); } function bindCurrentContainers() { bindSelectContainer(document.getElementById('orderform-category')); bindSelectContainer(document.getElementById('orderform-service')); } bindCurrentContainers(); if (typeof MutationObserver === 'function') { containerObserver = new MutationObserver(function () { bindCurrentContainers(); }); containerObserver.observe(form, { childList: true, subtree: true }); } addEventListener(document, 'pointerdown', function (event) { var target = event.target || event.srcElement, option = closestElement(target, '#select2-orderform-category-results .select2-results__option, #select2-orderform-service-results .select2-results__option'); if (option) { dropdownHovered = false; } }, true); addEventListener(document, 'keydown', function (event) { if (event.key === 'Escape' || event.keyCode === 27) { window.clearTimeout(openTimer); window.clearTimeout(closeTimer); dropdownHovered = false; activeSelect = null; } }, true); } function bindDripFeedEntryPoint() { var query = window.location.search || '', attempts = 0, maxAttempts = 24, retryTimer = null; if (!/(?:^|[?&])drip-feed=1(?:&|$)/.test(query)) { return; } function revealDripFeed() { var input = document.querySelector('#field-orderform-fields-check, input[name="OrderForm[check]"]'), field; attempts += 1; if (!input) { if (attempts < maxAttempts) { retryTimer = window.setTimeout(revealDripFeed, 250); } return; } window.clearTimeout(retryTimer); if (!input.checked && !input.disabled && typeof input.click === 'function') { input.click(); } field = input.closest ? input.closest('.form-group') : input.parentNode; if (!field) { return; } field.classList.add('lm-drip-entry-target'); window.setTimeout(function () { if (typeof field.scrollIntoView === 'function') { field.scrollIntoView({ behavior: reducedMotionQuery && reducedMotionQuery.matches ? 'auto' : 'smooth', block: 'center' }); } if (typeof input.focus === 'function') { input.focus(); } }, reducedMotionQuery && reducedMotionQuery.matches ? 0 : 180); } revealDripFeed(); } function bindDripFeedCancelDialog() { var dialog = document.getElementById('lm-drip-cancel-dialog'), panel, confirmLink, idSlot, triggers, closeControls, lastTrigger = null, isOpen = false; if (!dialog || dialog.getAttribute('data-lm-bound') === 'true') { return; } panel = dialog.querySelector('.lm-drip-dialog__panel'); confirmLink = dialog.querySelector('[data-lm-drip-dialog-confirm]'); idSlot = dialog.querySelector('[data-lm-drip-dialog-id]'); triggers = document.querySelectorAll('.lm-drip-cancel-trigger'); closeControls = dialog.querySelectorAll('[data-lm-drip-dialog-close]'); if (!panel || !confirmLink || !triggers.length) { return; } dialog.setAttribute('data-lm-bound', 'true'); function getFocusableItems() { return dialog.querySelectorAll('a[href], button:not([disabled]), [tabindex]:not([tabindex="-1"])'); } function openDialog(trigger) { var cancelUrl = trigger.getAttribute('href'), dripId = trigger.getAttribute('data-lm-drip-id') || ''; if (!cancelUrl) { return; } lastTrigger = trigger; confirmLink.setAttribute('href', cancelUrl); confirmLink.removeAttribute('aria-disabled'); confirmLink.classList.remove('is-committing'); if (idSlot) { idSlot.textContent = dripId; } dialog.classList.add('is-open'); dialog.setAttribute('aria-hidden', 'false'); root.classList.add('lm-drip-dialog-open'); isOpen = true; window.setTimeout(function () { panel.focus(); }, reducedMotionQuery && reducedMotionQuery.matches ? 0 : 120); } function closeDialog() { if (!isOpen) { return; } dialog.classList.remove('is-open'); dialog.setAttribute('aria-hidden', 'true'); root.classList.remove('lm-drip-dialog-open'); isOpen = false; if (lastTrigger && typeof lastTrigger.focus === 'function') { window.setTimeout(function () { lastTrigger.focus(); }, reducedMotionQuery && reducedMotionQuery.matches ? 0 : 240); } } Array.prototype.forEach.call(triggers, function (trigger) { addEventListener(trigger, 'click', function (event) { event.preventDefault(); openDialog(trigger); }); }); Array.prototype.forEach.call(closeControls, function (control) { addEventListener(control, 'click', function (event) { event.preventDefault(); closeDialog(); }); }); addEventListener(confirmLink, 'click', function (event) { if (confirmLink.getAttribute('aria-disabled') === 'true') { event.preventDefault(); return; } confirmLink.setAttribute('aria-disabled', 'true'); confirmLink.classList.add('is-committing'); }); addEventListener(document, 'keydown', function (event) { var focusable, first, last; if (!isOpen) { return; } if (event.key === 'Escape' || event.keyCode === 27) { event.preventDefault(); closeDialog(); return; } if (event.key !== 'Tab' && event.keyCode !== 9) { return; } focusable = getFocusableItems(); if (!focusable.length) { event.preventDefault(); panel.focus(); return; } first = focusable[0]; last = focusable[focusable.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } }, true); } function findClosest(element, selector) { while (element && element !== document) { if (element.matches && element.matches(selector)) { return element; } element = element.parentNode; } return null; } function showCustomerToast(message) { var region = document.querySelector('.lm-customer-toast-region'), toast; if (!message) { return; } if (!region) { region = document.createElement('div'); region.className = 'lm-customer-toast-region'; region.setAttribute('aria-live', 'polite'); region.setAttribute('aria-atomic', 'true'); document.body.appendChild(region); } toast = document.createElement('div'); toast.className = 'lm-customer-toast'; toast.setAttribute('role', 'status'); toast.innerHTML = ''; toast.lastChild.textContent = message; region.appendChild(toast); window.setTimeout(function () { toast.classList.add('is-visible'); }, 16); window.setTimeout(function () { toast.classList.remove('is-visible'); window.setTimeout(function () { if (toast.parentNode) { toast.parentNode.removeChild(toast); } }, 260); }, 2200); } function copyCustomerText(value, successCopy, trigger) { var text = String(value || '').replace(/^\s+|\s+$/g, ''), fallback; function finish() { var icon; if (trigger) { trigger.classList.add('is-copied'); icon = trigger.querySelector('i'); if (icon) { icon.setAttribute('data-lm-original-class', icon.className); icon.className = 'fas fa-check'; } window.setTimeout(function () { trigger.classList.remove('is-copied'); if (icon && icon.getAttribute('data-lm-original-class')) { icon.className = icon.getAttribute('data-lm-original-class'); icon.removeAttribute('data-lm-original-class'); } }, 1300); } showCustomerToast(successCopy || 'Copied'); } function fallbackCopy() { fallback = document.createElement('textarea'); fallback.value = text; fallback.setAttribute('readonly', 'readonly'); fallback.style.position = 'fixed'; fallback.style.opacity = '0'; document.body.appendChild(fallback); fallback.select(); try { document.execCommand('copy'); finish(); } catch (ignore) { return; } finally { if (fallback.parentNode) { fallback.parentNode.removeChild(fallback); } } } if (!text) { return; } if (navigator.clipboard && navigator.clipboard.writeText && window.isSecureContext) { navigator.clipboard.writeText(text).then(finish, fallbackCopy); } else { fallbackCopy(); } } function bindCopyUtilities() { addEventListener(document, 'click', function (event) { var trigger = findClosest(event.target || event.srcElement, '[data-lm-copy-value], [data-lm-copy-target], [data-lm-quote-target]'), target, value, reply; if (!trigger) { return; } preventDefault(event); if (trigger.getAttribute('data-lm-quote-target')) { target = document.querySelector(trigger.getAttribute('data-lm-quote-target')); reply = document.querySelector('[data-lm-ticket-reply] textarea'); if (target && reply) { value = String(target.textContent || '').replace(/^\s+|\s+$/g, '').replace(/^/gm, '> '); reply.value = (reply.value ? reply.value.replace(/\s+$/g, '') + '\n\n' : '') + value + '\n\n'; reply.focus(); reply.dispatchEvent(new Event('input', { bubbles: true })); } return; } if (trigger.getAttribute('data-lm-copy-target')) { target = document.querySelector(trigger.getAttribute('data-lm-copy-target')); value = target ? (typeof target.value === 'string' ? target.value : target.textContent) : ''; } else { value = trigger.getAttribute('data-lm-copy-value'); } copyCustomerText(value, trigger.getAttribute('data-lm-copy-success'), trigger); }); } function bindOrderSelection() { var workspace = document.querySelector('[data-lm-order-workspace]'), allToggle, checks, count, copyButton, label, lastIndex = -1, index; if (!workspace) { return; } allToggle = workspace.querySelector('[data-lm-select-all]'); checks = workspace.querySelectorAll('[data-lm-order-select]'); count = workspace.querySelector('[data-lm-selection-count]'); copyButton = workspace.querySelector('[data-lm-copy-selected]'); label = count ? String(count.textContent || '').replace(/[\s\d]+$/g, '') : 'Selected'; function selectedValues() { var values = [], checkIndex; for (checkIndex = 0; checkIndex < checks.length; checkIndex += 1) { if (checks[checkIndex].checked) { values.push(checks[checkIndex].value); checks[checkIndex].closest('tr').classList.add('is-selected'); } else { checks[checkIndex].closest('tr').classList.remove('is-selected'); } } return values; } function updateSelection() { var values = selectedValues(); if (count) { count.textContent = label + ' ' + values.length; } if (copyButton) { copyButton.disabled = values.length === 0; } if (allToggle) { allToggle.checked = values.length > 0 && values.length === checks.length; allToggle.indeterminate = values.length > 0 && values.length < checks.length; } } if (allToggle) { addEventListener(allToggle, 'change', function () { var toggleIndex; for (toggleIndex = 0; toggleIndex < checks.length; toggleIndex += 1) { checks[toggleIndex].checked = allToggle.checked; } updateSelection(); }); } for (index = 0; index < checks.length; index += 1) { (function (currentIndex) { addEventListener(checks[currentIndex], 'click', function (event) { var start, end, rangeIndex; if (event.shiftKey && lastIndex >= 0) { start = Math.min(lastIndex, currentIndex); end = Math.max(lastIndex, currentIndex); for (rangeIndex = start; rangeIndex <= end; rangeIndex += 1) { checks[rangeIndex].checked = checks[currentIndex].checked; } } lastIndex = currentIndex; updateSelection(); }); }(index)); } addClickListener(copyButton, function () { var values = selectedValues(); if (values.length) { copyCustomerText(values.join(', '), workspace.getAttribute('data-lm-copied-copy'), copyButton); } }); updateSelection(); } function bindFundsPresets() { var group = document.querySelector('[data-lm-funds-presets]'), amount = document.getElementById('amount'), buttons, index; if (!group || !amount) { return; } buttons = group.querySelectorAll('[data-lm-amount]'); for (index = 0; index < buttons.length; index += 1) { addClickListener(buttons[index], function () { var buttonIndex; amount.value = this.getAttribute('data-lm-amount'); amount.dispatchEvent(new Event('input', { bubbles: true })); amount.focus(); for (buttonIndex = 0; buttonIndex < buttons.length; buttonIndex += 1) { buttons[buttonIndex].classList.toggle('is-active', buttons[buttonIndex] === this); } showCustomerToast(group.getAttribute('data-success-copy')); }); } } function bindPasswordStrength() { var panel = document.querySelector('[data-lm-password-strength]'), password = document.querySelector('[data-lm-password-new]'), confirm = document.querySelector('[data-lm-password-confirm]'), meter, label, match; if (!panel || !password || !confirm) { return; } meter = panel.querySelector('[data-lm-password-meter]'); label = panel.querySelector('[data-lm-password-label]'); match = panel.querySelector('[data-lm-password-match]'); function updateStrength() { var value = password.value, score = 0, state = 'low'; if (value.length >= 8) { score += 1; } if (value.length >= 12) { score += 1; } if (/[a-z]/.test(value) && /[A-Z]/.test(value)) { score += 1; } if (/\d/.test(value)) { score += 1; } if (/[^A-Za-z0-9]/.test(value)) { score += 1; } if (score >= 4) { state = 'high'; } else if (score >= 2) { state = 'mid'; } panel.setAttribute('data-strength', state); meter.style.width = (state === 'high' ? 100 : state === 'mid' ? 62 : value ? 28 : 0) + '%'; label.textContent = panel.getAttribute('data-' + state); if (confirm.value) { match.textContent = confirm.value === value ? panel.getAttribute('data-match') : panel.getAttribute('data-no-match'); match.className = confirm.value === value ? 'is-match' : 'is-mismatch'; } else { match.textContent = ''; match.className = ''; } } addEventListener(password, 'input', updateStrength); addEventListener(confirm, 'input', updateStrength); updateStrength(); } function setupTicketDraft(form, textarea, subject, storageSuffix) { var status = form.querySelector('[data-lm-ticket-draft-status]'), count = form.querySelector('[data-lm-ticket-char-count]'), clear = form.querySelector('[data-lm-ticket-clear-draft]'), storageKey = 'lm-ticket-draft::' + window.location.pathname + '::' + storageSuffix, timer, restored, draft; function updateCount() { if (count) { count.textContent = textarea.value.length; } } function saveDraft() { try { window.sessionStorage.setItem(storageKey, JSON.stringify({ message: textarea.value, subject: subject ? subject.value : '' })); if (status) { status.textContent = form.getAttribute('data-lm-draft-saved') || ''; } } catch (ignore) { return; } } function scheduleSave() { window.clearTimeout(timer); updateCount(); timer = window.setTimeout(saveDraft, 420); } if (form.getAttribute('data-lm-ticket-success') === '1') { try { window.sessionStorage.removeItem(storageKey); } catch (ignoreSuccess) { return; } } else if (!textarea.value) { try { restored = window.sessionStorage.getItem(storageKey); draft = restored ? JSON.parse(restored) : null; if (draft && draft.message) { textarea.value = draft.message; if (subject && !subject.value && draft.subject) { subject.value = draft.subject; } if (status) { status.textContent = form.getAttribute('data-lm-draft-restored') || form.getAttribute('data-lm-draft-saved') || ''; } } } catch (ignoreRestore) { draft = null; } } addEventListener(textarea, 'input', scheduleSave); if (subject) { addEventListener(subject, 'input', scheduleSave); } addClickListener(clear, function () { textarea.value = ''; if (subject) { subject.value = ''; } try { window.sessionStorage.removeItem(storageKey); } catch (ignoreClear) { return; } if (status) { status.textContent = ''; } updateCount(); textarea.focus(); }); updateCount(); } function bindTicketComfortTools() { var form = document.querySelector('[data-lm-ticket-compose]'), replyForm = document.querySelector('[data-lm-ticket-reply]'), textarea, subject, templateButtons, orderInput, insertOrder, index; if (form) { textarea = form.querySelector('textarea[name="TicketForm[message]"]'); subject = form.querySelector('input[name="TicketForm[subject]"]'); templateButtons = form.querySelectorAll('[data-lm-ticket-template]'); orderInput = form.querySelector('#lm-ticket-order-id'); insertOrder = form.querySelector('[data-lm-ticket-insert-order]'); if (textarea) { setupTicketDraft(form, textarea, subject, 'new'); } for (index = 0; index < templateButtons.length; index += 1) { addClickListener(templateButtons[index], function () { var key = this.getAttribute('data-lm-ticket-template'), template = form.getAttribute('data-lm-template-' + key) || ''; template = template.replace(/\\n/g, '\n'); if (textarea) { textarea.value = textarea.value ? textarea.value.replace(/\s+$/g, '') + '\n\n' + template : template; textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.focus(); } if (subject && !subject.value) { subject.value = this.textContent.replace(/^\s+|\s+$/g, ''); subject.dispatchEvent(new Event('input', { bubbles: true })); } }); } addClickListener(insertOrder, function () { var value = orderInput ? orderInput.value.replace(/[^0-9]/g, '') : '', label = orderInput && orderInput.parentNode && orderInput.parentNode.parentNode.querySelector('label'); if (!value || !textarea) { if (orderInput) { orderInput.focus(); } return; } textarea.value = (textarea.value ? textarea.value.replace(/\s+$/g, '') + '\n' : '') + (label ? label.textContent : 'Order ID') + ': #' + value + '\n'; textarea.dispatchEvent(new Event('input', { bubbles: true })); textarea.focus(); }); } if (replyForm) { textarea = replyForm.querySelector('textarea'); if (textarea) { setupTicketDraft(replyForm, textarea, null, replyForm.getAttribute('data-lm-ticket-id') || 'reply'); } } } function bindNewOrderPasteAssist() { var form = document.getElementById('order-form'), fields = document.getElementById('fields'), observer; if (!form || !fields) { return; } function enhance() { var input = fields.querySelector('input[name="OrderForm[link]"], input[id*="link"]'), group, button, hint, chargeLabel; if (input && !input.getAttribute('data-lm-paste-enhanced')) { input.setAttribute('data-lm-paste-enhanced', 'true'); group = input.parentNode; group.classList.add('lm-link-field-enhanced'); button = document.createElement('button'); button.type = 'button'; button.className = 'lm-link-paste-button'; button.setAttribute('aria-label', form.getAttribute('data-lm-link-paste') || 'Paste link'); button.innerHTML = '' + (form.getAttribute('data-lm-link-paste') || 'Paste') + ''; group.appendChild(button); hint = document.createElement('small'); hint.className = 'lm-link-field-hint'; hint.innerHTML = ''; hint.lastChild.textContent = form.getAttribute('data-lm-link-hint') || ''; group.appendChild(hint); addClickListener(button, function () { function applyPasted(value) { value = String(value || '').replace(/^\s+|\s+$/g, ''); if (!value) { input.focus(); return; } input.value = value; input.dispatchEvent(new Event('input', { bubbles: true })); input.dispatchEvent(new Event('change', { bubbles: true })); input.focus(); button.classList.add('is-pasted'); window.setTimeout(function () { button.classList.remove('is-pasted'); }, 1000); } if (navigator.clipboard && navigator.clipboard.readText && window.isSecureContext) { navigator.clipboard.readText().then(applyPasted, function () { input.focus(); showCustomerToast(form.getAttribute('data-lm-link-paste-denied')); }); } else { input.focus(); showCustomerToast(form.getAttribute('data-lm-link-paste-denied')); } }); } chargeLabel = form.querySelector('label[for="charge"]'); if (chargeLabel && !chargeLabel.querySelector('.lm-auto-price-hint')) { hint = document.createElement('small'); hint.className = 'lm-auto-price-hint'; hint.textContent = form.getAttribute('data-lm-auto-price') || ''; chargeLabel.appendChild(hint); } } enhance(); if (window.MutationObserver) { observer = new MutationObserver(enhance); observer.observe(fields, { childList: true, subtree: true }); } } function bindRecentOrders() { var panel = document.querySelector('[data-lm-recent-orders]'), list, updated, refreshButton, limitButtons, endpoint, limit, timer, loading = false, rankPanel = document.querySelector('[data-lm-member-rank]'), index; if (!panel || !window.fetch || !window.DOMParser) { return; } list = panel.querySelector('[data-lm-recent-list]'); updated = panel.querySelector('[data-lm-recent-updated]'); refreshButton = panel.querySelector('[data-lm-recent-refresh]'); limitButtons = panel.querySelectorAll('[data-lm-recent-limit]'); endpoint = panel.getAttribute('data-endpoint') || '/orders'; limit = parseInt(panel.getAttribute('data-limit'), 10) || 10; try { limit = parseInt(window.localStorage.getItem('lm-recent-orders-limit'), 10) || limit; } catch (ignoreLimit) { limit = limit; } function updateRank(items) { var active = 0, completed = 0, score, rankIndex, names = ['Starter', 'Rising', 'Live Pro', 'Studio'], itemIndex, name, progress, scoreElement; if (!rankPanel) { return; } for (itemIndex = 0; itemIndex < items.length; itemIndex += 1) { if (/pending|processing|inprogress/.test(items[itemIndex].state)) { active += 1; } if (items[itemIndex].state === 'completed') { completed += 1; } } score = Math.min(100, items.length * 6 + active * 8 + completed * 3); rankIndex = score >= 82 ? 3 : score >= 56 ? 2 : score >= 25 ? 1 : 0; name = rankPanel.querySelector('[data-lm-rank-name]'); progress = rankPanel.querySelector('[data-lm-rank-progress]'); scoreElement = rankPanel.querySelector('[data-lm-rank-score]'); if (name) { name.textContent = names[rankIndex]; } if (progress) { progress.style.width = score + '%'; } if (scoreElement) { scoreElement.textContent = score; } rankPanel.setAttribute('data-rank', String(rankIndex)); } function parseOrders(html) { var doc = new DOMParser().parseFromString(html, 'text/html'), rows = doc.querySelectorAll('.lm-orders-table tbody tr[data-order-state]'), items = [], rowIndex, row, text; for (rowIndex = 0; rowIndex < rows.length && rowIndex < limit; rowIndex += 1) { row = rows[rowIndex]; text = function (selector) { var element = row.querySelector(selector); return element ? String(element.textContent || '').replace(/^\s+|\s+$/g, '') : ''; }; items.push({ id: row.getAttribute('data-order-id') || text('.lm-order-id').replace(/[^0-9]/g, ''), state: row.getAttribute('data-order-state') || 'neutral', status: text('.lm-status-badge__text'), service: text('.lm-order-service'), date: text('.lm-order-date'), quantity: text('.lm-order-quantity'), remains: text('.lm-order-remains strong') }); } return items; } function render(items) { var fragment = document.createDocumentFragment(), itemIndex, item, link, top, id, status, service, meta; list.innerHTML = ''; if (!items.length) { service = document.createElement('p'); service.className = 'lm-recent-orders__empty'; service.textContent = panel.getAttribute('data-empty-copy') || ''; list.appendChild(service); updateRank(items); return; } for (itemIndex = 0; itemIndex < items.length; itemIndex += 1) { item = items[itemIndex]; link = document.createElement('a'); link.className = 'lm-recent-order lm-recent-order--' + item.state; link.href = endpoint + '?search=' + encodeURIComponent(item.id); top = document.createElement('span'); top.className = 'lm-recent-order__top'; id = document.createElement('strong'); id.textContent = '#' + item.id; status = document.createElement('span'); status.className = 'lm-status-badge lm-status-badge--' + item.state; status.innerHTML = ''; status.lastChild.textContent = item.status; top.appendChild(id); top.appendChild(status); service = document.createElement('span'); service.className = 'lm-recent-order__service'; service.textContent = item.service; meta = document.createElement('span'); meta.className = 'lm-recent-order__meta'; meta.innerHTML = ''; meta.children[0].textContent = item.date; meta.children[1].textContent = item.quantity; link.appendChild(top); link.appendChild(service); link.appendChild(meta); fragment.appendChild(link); } list.appendChild(fragment); updateRank(items); } function loadOrders() { if (loading || document.hidden) { return; } loading = true; panel.classList.add('is-loading'); if (refreshButton) { refreshButton.classList.add('is-loading'); } window.fetch(endpoint, { credentials: 'same-origin', cache: 'no-store', headers: { 'X-Requested-With': 'LiveMalaysiaRecentOrders' } }) .then(function (response) { if (!response.ok) { throw new Error('orders'); } return response.text(); }) .then(function (html) { render(parseOrders(html)); if (updated) { updated.textContent = (panel.getAttribute('data-updated-copy') || '') + ' ' + new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); } panel.classList.remove('has-error'); }) .catch(function () { list.innerHTML = '

'; list.firstChild.textContent = panel.getAttribute('data-error-copy') || ''; panel.classList.add('has-error'); }) .then(function () { loading = false; panel.classList.remove('is-loading'); if (refreshButton) { refreshButton.classList.remove('is-loading'); } }); } function applyLimit(nextLimit) { var buttonIndex; limit = nextLimit; for (buttonIndex = 0; buttonIndex < limitButtons.length; buttonIndex += 1) { limitButtons[buttonIndex].classList.toggle('is-active', parseInt(limitButtons[buttonIndex].getAttribute('data-lm-recent-limit'), 10) === limit); } try { window.localStorage.setItem('lm-recent-orders-limit', String(limit)); } catch (ignoreStorage) { limit = limit; } loadOrders(); } for (index = 0; index < limitButtons.length; index += 1) { addClickListener(limitButtons[index], function () { applyLimit(parseInt(this.getAttribute('data-lm-recent-limit'), 10) || 10); }); } addClickListener(refreshButton, loadOrders); addEventListener(document, 'visibilitychange', function () { if (!document.hidden) { loadOrders(); } }); applyLimit(limit); timer = window.setInterval(loadOrders, 30000); addEventListener(window, 'beforeunload', function () { window.clearInterval(timer); }); } function bindServiceCompare() { var panel = document.querySelector('[data-lm-service-compare-panel]'), itemsContainer, checks, clear, index; if (!panel) { return; } itemsContainer = panel.querySelector('[data-lm-service-compare-items]'); checks = document.querySelectorAll('[data-lm-service-compare]'); clear = panel.querySelector('[data-lm-service-compare-clear]'); function selectedChecks() { var selected = [], checkIndex; for (checkIndex = 0; checkIndex < checks.length; checkIndex += 1) { if (checks[checkIndex].checked) { selected.push(checks[checkIndex]); } } return selected; } function renderCompare() { var selected = selectedChecks(), fragment = document.createDocumentFragment(), selectedIndex, row, card, title, metrics, action; itemsContainer.innerHTML = ''; panel.classList.toggle('is-active', selected.length > 0); if (!selected.length) { card = document.createElement('p'); card.textContent = panel.getAttribute('data-empty-copy') || ''; itemsContainer.appendChild(card); return; } for (selectedIndex = 0; selectedIndex < selected.length; selectedIndex += 1) { row = selected[selectedIndex].closest('[data-lm-service-row]'); row.classList.add('is-comparing'); card = document.createElement('article'); card.className = 'lm-service-compare-card'; title = document.createElement('strong'); title.textContent = '#' + row.getAttribute('data-service-id') + ' · ' + row.getAttribute('data-service-name'); metrics = document.createElement('span'); metrics.innerHTML = ''; metrics.children[0].textContent = row.getAttribute('data-service-rate'); metrics.children[1].textContent = row.getAttribute('data-service-min') + ' — ' + row.getAttribute('data-service-max'); metrics.children[2].textContent = row.getAttribute('data-service-average') || '—'; action = document.createElement('a'); action.href = (panel.getAttribute('data-order-url') || '/') + '?service=' + encodeURIComponent(row.getAttribute('data-service-id')); action.innerHTML = ''; card.appendChild(title); card.appendChild(metrics); card.appendChild(action); fragment.appendChild(card); } itemsContainer.appendChild(fragment); } for (index = 0; index < checks.length; index += 1) { addEventListener(checks[index], 'change', function () { var rowIndex, selected = selectedChecks(); if (selected.length > 3) { this.checked = false; showCustomerToast(panel.querySelector('.lm-service-compare__head span').textContent); } for (rowIndex = 0; rowIndex < checks.length; rowIndex += 1) { if (!checks[rowIndex].checked) { checks[rowIndex].closest('[data-lm-service-row]').classList.remove('is-comparing'); } } renderCompare(); }); } addClickListener(clear, function () { var checkIndex; for (checkIndex = 0; checkIndex < checks.length; checkIndex += 1) { checks[checkIndex].checked = false; checks[checkIndex].closest('[data-lm-service-row]').classList.remove('is-comparing'); } renderCompare(); }); renderCompare(); } function bindInlineAcademy() { var panel = document.querySelector('[data-lm-inline-guide-panel]'), buttons = document.querySelectorAll('[data-lm-inline-guide]'), contents, close, index; if (!panel || !buttons.length) { return; } contents = panel.querySelectorAll('[data-lm-inline-guide-content]'); close = panel.querySelector('[data-lm-inline-guide-close]'); function hidePanel() { var buttonIndex; panel.hidden = true; for (buttonIndex = 0; buttonIndex < buttons.length; buttonIndex += 1) { buttons[buttonIndex].setAttribute('aria-expanded', 'false'); buttons[buttonIndex].classList.remove('is-active'); } } for (index = 0; index < buttons.length; index += 1) { addClickListener(buttons[index], function () { var key = this.getAttribute('data-lm-inline-guide'), contentIndex, buttonIndex; panel.hidden = false; for (contentIndex = 0; contentIndex < contents.length; contentIndex += 1) { contents[contentIndex].hidden = contents[contentIndex].getAttribute('data-lm-inline-guide-content') !== key; } for (buttonIndex = 0; buttonIndex < buttons.length; buttonIndex += 1) { buttons[buttonIndex].classList.toggle('is-active', buttons[buttonIndex] === this); buttons[buttonIndex].setAttribute('aria-expanded', buttons[buttonIndex] === this ? 'true' : 'false'); } }); } addClickListener(close, hidePanel); } function bindPageLoader() { var loader = document.getElementById('lm-page-loader'), startedAt = window.__lmPageLoadStarted || new Date().getTime(), minimumVisible = reducedMotionQuery && reducedMotionQuery.matches ? 180 : 680, leaving = false, failSafeTimer; function hideLoader() { if (!loader || leaving) { return; } leaving = true; window.clearTimeout(failSafeTimer); loader.classList.add('is-leaving'); loader.setAttribute('aria-hidden', 'true'); root.removeAttribute('aria-busy'); window.setTimeout(function () { root.classList.remove('lm-page-loading'); if (loader.parentNode) { loader.parentNode.removeChild(loader); } }, reducedMotionQuery && reducedMotionQuery.matches ? 20 : 540); } function finishLoader() { var elapsed = new Date().getTime() - startedAt; window.setTimeout(hideLoader, Math.max(0, minimumVisible - elapsed)); } if (!loader) { root.classList.remove('lm-page-loading'); return; } root.setAttribute('aria-busy', 'true'); failSafeTimer = window.setTimeout(hideLoader, 6500); if (document.readyState === 'complete') { finishLoader(); } else { addEventListener(window, 'load', finishLoader); } } function onReady(callback) { if (document.readyState === 'loading') { if (document.addEventListener) { document.addEventListener('DOMContentLoaded', callback, false); } else if (window.attachEvent) { window.attachEvent('onload', callback); } } else { callback(); } } function initializePageInteractions() { bindPageLoader(); bindSidebarRail(); bindControls(); bindFontMenu(); bindHeaderUtilityMenus(); bindBalanceWallet(); bindTicketAlertDismiss(); bindPointerEffects(); bindLiveRoomAtmosphere(); bindNewOrderChoiceMotion(); bindNewOrderHoverSelects(); bindQuantityPresetAssist(); bindChargeCurrencyBoard(); bindDripFeedEntryPoint(); bindDripFeedCancelDialog(); bindCopyUtilities(); bindOrderSelection(); bindFundsPresets(); bindPasswordStrength(); bindTicketComfortTools(); bindNewOrderPasteAssist(); bindRecentOrders(); bindServiceCompare(); bindInlineAcademy(); } // Apply saved preferences immediately; DOM-dependent controls wait until markup is ready. applyTheme(currentTheme, false); applyFontScale(currentFontScale, false); bindSystemThemeListener(); bindScrollState(); onReady(initializePageInteractions); }());