# king-addons/51.1.49/includes/widgets/Interactive_Steps_Progress/script.js

King Addons for Elementor – 80+ Elementor Widgets, 4 000+ Elementor Templates, WooCommerce, Mega Menu, Popup Builder, version 51.1.49. 419 lines.

- Page: https://pluginprobe.com/plugins/king-addons/51.1.49/code/includes/widgets/Interactive_Steps_Progress/script.js
- Raw: https://pluginprobe.com/plugins/king-addons/51.1.49/raw/includes/widgets/Interactive_Steps_Progress/script.js
- Modified: 2026-01-06T16:52:16+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/king-addons/51.1.49/code/includes/widgets/Interactive_Steps_Progress/script.js#L10-L20`.

```javascript
"use strict";
(($) => {
    const clamp = (value, min, max) => Math.min(Math.max(value, min), max);

    const parseSettings = (root) => {
        if (!root || !root.dataset) {
            return {};
        }

        try {
            return JSON.parse(root.dataset.settings || "{}");
        } catch (e) {
            return {};
        }
    };

    const initWidget = ($scope) => {
        const root = $scope.find(".king-addons-interactive-steps").first()[0];
        if (!root || root.dataset.kngIspInit === "yes") {
            return;
        }

        root.dataset.kngIspInit = "yes";
        const options = parseSettings(root);

        const steps = Array.from(root.querySelectorAll(".king-addons-isp-step"));
        if (!steps.length) {
            return;
        }

        const progressCount = root.querySelector(".king-addons-isp-progress__count");
        const progressPercent = root.querySelector(".king-addons-isp-progress__percent");
        const progressFill = root.querySelector(".king-addons-isp-progress__fill");
        const stickySteps = root.querySelector(".king-addons-isp-progress__steps");
        const isStickyHeader = root.classList.contains("king-addons-isp-sticky-header");
        let stickyStepItems = [];
        const prefersReduced =
            window.matchMedia &&
            window.matchMedia("(prefers-reduced-motion: reduce)").matches;
        const isEditor =
            window.elementorFrontend &&
            elementorFrontend.isEditMode &&
            elementorFrontend.isEditMode();

        const reducedMode = options.reducedMotion || "auto";
        const reducedFull =
            reducedMode === "full" ||
            (prefersReduced && reducedMode !== "off" && reducedMode !== "minimal");
        const reducedMinimal =
            reducedMode === "minimal" ||
            (prefersReduced && reducedMode === "auto");

        if (reducedFull) {
            root.classList.add("king-addons-isp-reduced-full");
        } else if (reducedMinimal) {
            root.classList.add("king-addons-isp-reduced-minimal");
        }

        const expandBehavior = options.expandBehavior || "click";
        const defaultOpen = options.defaultOpen || "first";
        const singleOpen = options.singleOpen !== "no";
        const scrollToStep = options.scrollToStep === "yes";
        const markersClickable = options.markersClickable !== "no";
        const showStepCount = options.showStepCount === "yes";
        const showPercent = options.showPercent === "yes";
        const stepLabelTemplate = options.stepLabel || "Step %1$s of %2$s";
        const updateHash = options.updateHash === "yes";
        const scrollToHash = options.scrollToHash === "yes";
        const anchorLinking = options.anchorLinking === "yes";
        const keyboardNav = options.keyboardNav === "yes";
        const activateOnScroll = !isEditor && options.activateOnScroll === "yes";
        const reveal = options.reveal === "yes" && !isEditor;

        const getPanel = (step) => step.querySelector(".king-addons-isp-step__panel");
        const getButton = (step) => step.querySelector(".king-addons-isp-step__button");
        const getAnchor = (step) => step.dataset.anchor || "";

        const setExpanded = (step, isOpen) => {
            const panel = getPanel(step);
            const button = getButton(step);

            if (!panel || !button) {
                return;
            }

            step.classList.toggle("is-open", isOpen);
            button.setAttribute("aria-expanded", isOpen ? "true" : "false");
            panel.setAttribute("aria-hidden", isOpen ? "false" : "true");
            if (isOpen) {
                panel.removeAttribute("hidden");
            } else {
                panel.setAttribute("hidden", "hidden");
            }
        };

        const closeAll = () => {
            steps.forEach((step) => setExpanded(step, false));
        };

        const openAll = () => {
            steps.forEach((step) => setExpanded(step, true));
        };

        const applyDefaults = () => {
            if (expandBehavior === "none") {
                openAll();
                return;
            }

            closeAll();
            if (defaultOpen === "first") {
                const firstStep = steps[0];
                if (firstStep && getPanel(firstStep)) {
                    setExpanded(firstStep, true);
                }
            }
        };

        const formatStepLabel = (current, total) =>
            stepLabelTemplate
                .replace("%1$s", current)
                .replace("%2$s", total);

        const updateProgress = (activeIndex) => {
            const total = steps.length;
            const ratio = total > 1 ? activeIndex / (total - 1) : 1;
            const percentValue = Math.round(ratio * 100);

            root.style.setProperty("--kng-isp-progress-ratio", ratio);
            root.style.setProperty("--kng-isp-progress-percent", `${percentValue}%`);

            if (progressFill) {
                progressFill.style.width = `${percentValue}%`;
            }

            if (progressCount && showStepCount) {
                progressCount.textContent = formatStepLabel(
                    activeIndex + 1,
                    total
                );
            }

            if (progressPercent && showPercent) {
                progressPercent.textContent = `${percentValue}%`;
            }
        };

        const setActive = (activeIndex, shouldUpdateHash) => {
            steps.forEach((step, index) => {
                step.classList.toggle("is-active", index === activeIndex);
                step.classList.toggle("is-completed", index < activeIndex);
                step.classList.toggle("is-upcoming", index > activeIndex);

                if (index === activeIndex) {
                    step.setAttribute("aria-current", "step");
                } else {
                    step.removeAttribute("aria-current");
                }
            });

            updateProgress(activeIndex);

            if (stickyStepItems.length) {
                stickyStepItems.forEach((item, index) => {
                    item.classList.toggle("is-active", index === activeIndex);
                    item.classList.toggle("is-completed", index < activeIndex);

                    if (index === activeIndex) {
                        item.setAttribute("aria-current", "step");
                    } else {
                        item.removeAttribute("aria-current");
                    }
                });
            }

            if (shouldUpdateHash && updateHash && anchorLinking && !isEditor) {
                const anchor = getAnchor(steps[activeIndex]);
                if (anchor) {
                    try {
                        window.history.replaceState(null, "", `#${anchor}`);
                    } catch (e) {
                        window.location.hash = anchor;
                    }
                }
            }
        };

        const activateStep = (index, openPanel = true, shouldScroll = true) => {
            const step = steps[index];
            if (!step) {
                return;
            }

            const hasPanel = !!getPanel(step);
            if (expandBehavior === "click" && hasPanel && openPanel) {
                if (singleOpen) {
                    closeAll();
                    setExpanded(step, true);
                } else {
                    setExpanded(step, !step.classList.contains("is-open"));
                }
            }

            setActive(index, true);

            if (scrollToStep && shouldScroll && !isEditor) {
                step.scrollIntoView({
                    behavior: reducedFull ? "auto" : "smooth",
                    block: "start",
                });
            }
        };

        applyDefaults();

        if (stickySteps && isStickyHeader) {
            stickySteps.innerHTML = "";
            stickyStepItems = steps.map((_, index) => {
                const item = document.createElement("button");
                item.type = "button";
                item.className = "king-addons-isp-progress-step";
                item.dataset.stepIndex = String(index);
                item.setAttribute(
                    "aria-label",
                    formatStepLabel(index + 1, steps.length)
                );
                stickySteps.appendChild(item);
                return item;
            });
        }

        const initialActive = Math.max(
            0,
            steps.findIndex((step) => step.classList.contains("is-active"))
        );
        setActive(initialActive, false);

        root.addEventListener("click", (event) => {
            const button = event.target.closest(".king-addons-isp-step__button");
            if (button) {
                const step = button.closest(".king-addons-isp-step");
                const index = steps.indexOf(step);
                if (index > -1) {
                    activateStep(index, true, true);
                }
                return;
            }

            if (markersClickable) {
                const stickyDot = event.target.closest(".king-addons-isp-progress-step");
                if (stickyDot && stickyDot.dataset && stickyDot.dataset.stepIndex) {
                    const index = Number(stickyDot.dataset.stepIndex);
                    if (!Number.isNaN(index)) {
                        activateStep(clamp(index, 0, steps.length - 1), true, true);
                    }
                    return;
                }
            }

            if (markersClickable) {
                const marker = event.target.closest(".king-addons-isp-step__marker");
                if (marker) {
                    const step = marker.closest(".king-addons-isp-step");
                    const index = steps.indexOf(step);
                    if (index > -1) {
                        activateStep(index, true, true);
                    }
                }
            }
        });

        const isHorizontalLayout = () => {
            if (!root.classList.contains("king-addons-isp-layout-horizontal")) {
                return false;
            }

            if (
                root.classList.contains("king-addons-isp-mobile-vertical") &&
                window.innerWidth <= 767
            ) {
                return false;
            }

            return true;
        };

        if (keyboardNav) {
            root.addEventListener("keydown", (event) => {
                if (!event.target.closest(".king-addons-isp-step__button")) {
                    return;
                }

                if (!["ArrowDown", "ArrowUp", "ArrowLeft", "ArrowRight"].includes(event.key)) {
                    return;
                }

                const step = event.target.closest(".king-addons-isp-step");
                const index = steps.indexOf(step);
                if (index < 0) {
                    return;
                }

                const isHorizontal = isHorizontalLayout();
                const stepDelta =
                    event.key === "ArrowDown" || event.key === "ArrowRight" ? 1 : -1;

                if (
                    (isHorizontal && (event.key === "ArrowLeft" || event.key === "ArrowRight")) ||
                    (!isHorizontal && (event.key === "ArrowDown" || event.key === "ArrowUp"))
                ) {
                    event.preventDefault();
                    const nextIndex = clamp(index + stepDelta, 0, steps.length - 1);
                    const nextButton = getButton(steps[nextIndex]);
                    if (nextButton) {
                        nextButton.focus();
                    }
                    activateStep(nextIndex, true, true);
                }
            });
        }

        if (anchorLinking && scrollToHash && window.location.hash && !isEditor) {
            const hash = window.location.hash.replace("#", "");
            const targetIndex = steps.findIndex(
                (step) => getAnchor(step) === hash
            );
            if (targetIndex > -1) {
                setTimeout(() => {
                    activateStep(targetIndex, true, false);
                    steps[targetIndex].scrollIntoView({
                        behavior: reducedFull ? "auto" : "smooth",
                        block: "start",
                    });
                }, 150);
            }
        }

        if (reveal) {
            const revealType = options.revealType || "fade";
            root.classList.add(`king-addons-isp-reveal-${revealType}`);

            steps.forEach((step, idx) => {
                step.classList.add("is-reveal");
                if (options.revealStagger && !reducedFull) {
                    step.style.transitionDelay = `${idx * options.revealStagger}ms`;
                }
            });

            if (reducedFull) {
                steps.forEach((step) => step.classList.add("is-revealed"));
            } else if ("IntersectionObserver" in window) {
                const revealObserver = new IntersectionObserver(
                    (entries) => {
                        entries.forEach((entry) => {
                            if (entry.isIntersecting) {
                                entry.target.classList.add("is-revealed");
                                revealObserver.unobserve(entry.target);
                            }
                        });
                    },
                    { threshold: 0.2 }
                );

                steps.forEach((step) => revealObserver.observe(step));
            } else {
                steps.forEach((step) => step.classList.add("is-revealed"));
            }
        }

        if (activateOnScroll && "IntersectionObserver" in window) {
            const offset = clamp(Number(options.activationOffset) || 40, 10, 80);
            const rootMargin = `-${offset}% 0px -${100 - offset}% 0px`;

            const observer = new IntersectionObserver(
                (entries) => {
                    const visible = entries
                        .filter((entry) => entry.isIntersecting)
                        .sort(
                            (a, b) =>
                                b.intersectionRatio - a.intersectionRatio
                        );

                    if (!visible.length) {
                        return;
                    }

                    const index = steps.indexOf(visible[0].target);
                    if (index > -1) {
                        activateStep(index, true, false);
                    }
                },
                {
                    root: null,
                    rootMargin,
                    threshold: [0, 0.25, 0.5, 0.75, 1],
                }
            );

            steps.forEach((step) => observer.observe(step));
        }
    };

    $(window).on("elementor/frontend/init", () => {
        elementorFrontend.hooks.addAction(
            "frontend/element_ready/king-addons-interactive-steps-progress.default",
            ($scope) => {
                elementorFrontend.elementsHandler.addHandler(
                    elementorModules.frontend.handlers.Base.extend({
                        onInit: function () {
                            initWidget(this.$element);
                        },
                    }),
                    { $element: $scope }
                );
            }
        );
    });
})(jQuery);

```
