/*
 *  Document   : template.js
 *  Author     : pixelcave
 *  Description: UI Framework custom functionality
 *
 */

// Imports
import * as bootstrap from "bootstrap";
import SimpleBar from "simplebar";
import Helpers from "./helpers.js";

// Assignments
window.bootstrap = bootstrap;
window.SimpleBar = SimpleBar;

// Template
export default class Template {
  /*
   * Auto called when creating a new instance
   *
   */
  constructor(options) {
    this.onLoad(() => this._uiInit(options));
  }

  /*
   * Init all vital functionality
   *
   */
  _uiInit(options) {
    // Layout variables
    this._lHtml = document.documentElement;
    this._lBody = document.body;
    this._lpageLoader = document.getElementById("page-loader");
    this._lPage = document.getElementById("page-container");
    this._lSidebar = document.getElementById("sidebar");
    this._lSidebarScrollCon =
      this._lSidebar && this._lSidebar.querySelector(".js-sidebar-scroll");
    this._lSideOverlay = document.getElementById("side-overlay");
    this._lResize = false;
    this._lHeader = document.getElementById("page-header");
    this._lHeaderSearch = document.getElementById("page-header-search");
    this._lHeaderSearchInput = document.getElementById(
      "page-header-search-input"
    );
    this._lHeaderLoader = document.getElementById("page-header-loader");
    this._lMain = document.getElementById("main-container");
    this._lFooter = document.getElementById("page-footer");

    // Helper variables
    this._darkMode = options?.darkMode || "system";
    this._lSidebarScroll = false;
    this._lSideOverlayScroll = false;

    // Base UI Init
    this._uiHandleTheme();
    this._uiHandleDarkMode();
    this._uiHandleSidebars();
    this._uiHandleHeader();
    this._uiHandleNav();

    // API Init
    this._uiApiLayout();
    this._uiApiBlocks();

    // Init the following helpers by default
    this.helpers([
      "bs-tooltip",
      "bs-popover",
      "dm-toggle-class",
      "dm-year-copy",
      "dm-ripple",
    ]);

    // Page Loader (hide it)
    this._uiHandlePageLoader();
  }

  /*
   * Handles sidebar and side overlay scrolling functionality/styles
   *
   */
  _uiHandleSidebars(mode = "init") {
    let self = this;

    if (self._lSidebar || self._lSideOverlay) {
      if (mode === "init") {
        // Add 'side-trans-enabled' class to #page-container (enables sidebar and side overlay transition on open/close)
        // Fixes IE10, IE11 and Edge bug in which animation was executed on each page load
        self._lPage.classList.add("side-trans-enabled");

        // Remove side transitions on window resizing
        window.addEventListener("resize", () => {
          clearTimeout(self._lResize);

          self._lPage.classList.remove("side-trans-enabled");

          self._lResize = setTimeout(() => {
            self._lPage.classList.add("side-trans-enabled");
          }, 500);
        });

        // Init custom scrolling
        this._uiHandleSidebars("custom-scroll");
      } else if ((mode = "custom-scroll")) {
        // If .side-scroll is added to #page-container, enable custom scrolling
        if (self._lPage.classList.contains("side-scroll")) {
          // Init custom scrolling on Sidebar
          if (self._lSidebar && !self._lSidebarScroll) {
            self._lSidebarScroll = new SimpleBar(self._lSidebarScrollCon);
          }

          // Init custom scrolling on Side Overlay
          if (self._lSideOverlay && !self._lSideOverlayScroll) {
            self._lSideOverlayScroll = new SimpleBar(self._lSideOverlay);
          }
        }
      }
    }
  }

  /*
   * Handles header related classes
   *
   */
  _uiHandleHeader() {
    let self = this;

    // If the header is fixed and has the glass style, add the related class on scrolling to add a background color to the header
    if (
      self._lPage.classList.contains("page-header-glass") &&
      self._lPage.classList.contains("page-header-fixed")
    ) {
      window.addEventListener("scroll", (e) => {
        if (window.scrollY > 60) {
          self._lPage.classList.add("page-header-scroll");
        } else {
          self._lPage.classList.remove("page-header-scroll");
        }
      });

      window.dispatchEvent(new CustomEvent("scroll"));
    }
  }

  /*
   * Toggle Submenu functionality
   *
   */
  _uiHandleNav() {
    let links = document.querySelectorAll('[data-toggle="submenu"]');

    // When a submenu link is clicked
    if (links) {
      links.forEach((link) => {
        link.addEventListener("click", (e) => {
          e.preventDefault();

          // Get main navigation
          let mainNav = link.closest(".nav-main");

          // Check if we are in horizontal navigation, large screen and hover is enabled
          if (
            !(
              (window.innerWidth ||
                document.documentElement.clientWidth ||
                document.body.clientWidth) > 991 &&
              mainNav.classList.contains("nav-main-horizontal") &&
              mainNav.classList.contains("nav-main-hover")
            )
          ) {
            // Get link's parent
            let parentLi = link.closest("li");

            if (parentLi.classList.contains("open")) {
              // If submenu is open, close it..
              parentLi.classList.remove("open");

              link.setAttribute("aria-expanded", "false");
            } else {
              // .. else if submenu is closed, close all other (same level) submenus first before open it
              Array.from(link.closest("ul").children).forEach((el) => {
                el.classList.remove("open");
              });

              parentLi.classList.add("open");

              link.setAttribute("aria-expanded", "true");
            }
          }

          return false;
        });
      });
    }
  }

  /*
   * Page loading screen functionality
   *
   */
  _uiHandlePageLoader(mode = "hide", colorClass) {
    if (mode === "show") {
      if (this._lpageLoader) {
        if (colorClass) {
          this._lpageLoader.className = "";
          this._lpageLoader.classList.add(colorClass);
        }

        this._lpageLoader.classList.add("show");
      } else {
        let pageLoader = document.createElement("div");

        pageLoader.id = "page-loader";

        if (colorClass) {
          pageLoader.classList.add(colorClass);
        }

        pageLoader.classList.add("show");

        this._lPage.insertBefore(pageLoader, this._lPage.firstChild);

        this._lpageLoader = document.getElementById("page-loader");
      }
    } else if (mode === "hide") {
      if (this._lpageLoader) {
        this._lpageLoader.classList.remove("show");
      }
    }
  }

  /*
   * Handles Dark Mode functionality
   *
   */
  _uiHandleDarkMode(mode) {
    let self = this;
    let rememberDarkMode = !self._lHtml.classList.contains(
      "dark-custom-defined"
    );

    // Get dark mode toggle buttons
    let btnLight = document.querySelector('[data-dark-mode="off"]');
    let btnDark = document.querySelector('[data-dark-mode="on"]');
    let btnSystem = document.querySelector('[data-dark-mode="system"]');

    // Get dark mode icon
    let darkModeIcon = document.querySelector("[data-dark-mode-icon]");

    // Set Dark Mode preference
    if (["on", "off", "system"].includes(mode)) {
      if (mode === "on") {
        self._lHtml.classList.add("dark");
        self._darkMode = "on";

        // Save dark mode preference
        if (rememberDarkMode) {
          localStorage.setItem("dashmixDarkMode", "on");
        }

        // Set active buttons
        btnLight?.classList.remove("active");
        btnDark?.classList.add("active");
        btnSystem?.classList.remove("active");

        // Set dark mode icon
        if (darkModeIcon) {
          darkModeIcon.className = "fa fa-fw fa-moon";
        }
      } else if (mode === "off") {
        self._lHtml.classList.remove("dark");
        self._darkMode = "off";

        // Save dark mode preference
        if (rememberDarkMode) {
          localStorage.setItem("dashmixDarkMode", "off");
        }

        // Set active buttons
        btnLight?.classList.add("active");
        btnDark?.classList.remove("active");
        btnSystem?.classList.remove("active");

        // Set dark mode icon
        if (darkModeIcon) {
          darkModeIcon.className = "far fa-fw fa-moon";
        }
      } else if (mode === "system") {
        if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
          self._lHtml.classList.add("dark");

          // Set dark mode icon
          if (darkModeIcon) {
            darkModeIcon.className = "fa fa-fw fa-moon";
          }
        } else {
          self._lHtml.classList.remove("dark");

          // Set dark mode icon
          if (darkModeIcon) {
            darkModeIcon.className = "far fa-fw fa-moon";
          }
        }

        self._darkMode = "system";

        // Save dark mode preference
        if (rememberDarkMode) {
          localStorage.setItem("dashmixDarkMode", "system");
        }

        // Set active buttons
        btnLight?.classList.remove("active");
        btnDark?.classList.remove("active");
        btnSystem?.classList.add("active");
      }
    } else {
      // We are in init state
      let darkMode = "";

      // If the preference is saved before, else use the default
      if (rememberDarkMode) {
        darkMode = localStorage.getItem("dashmixDarkMode") || self._darkMode;
      } else {
        localStorage.removeItem("dashmixDarkMode");
        darkMode = self._darkMode;
      }

      // Set Dark Mode Preference
      if (darkMode === "on") {
        self._uiHandleDarkMode("on");
      } else if (darkMode === "off") {
        self._uiHandleDarkMode("off");
      } else if (darkMode === "system") {
        self._uiHandleDarkMode("system");
      }

      // If dark mode preference is changed on system, update it accordingly if dark mode is set to "system"
      window
        .matchMedia("(prefers-color-scheme: dark)")
        .addEventListener("change", () => {
          if (self._darkMode === "system") {
            self._uiHandleDarkMode("system");
          }
        });
    }
  }

  /*
   * Set active color theme functionality
   *
   */
  _uiHandleTheme() {
    let self = this;
    let themeEl = document.getElementById("css-theme");
    let rememberTheme = this._lHtml.classList.contains("remember-theme");

    // If remember theme is enabled
    if (rememberTheme) {
      let themeName = localStorage.getItem("dashmixColorTheme") || false;

      // Update color theme
      if (themeName) {
        self._uiUpdateTheme(themeEl, themeName);
      }

      // Update theme element
      themeEl = document.getElementById("css-theme");
    } else {
      localStorage.removeItem("dashmixColorTheme");
    }

    // Set the active color theme link as active
    document
      .querySelectorAll(
        '[data-toggle="theme"][data-theme="' +
          (themeEl ? themeEl.getAttribute("href") : "default") +
          '"]'
      )
      .forEach((link) => {
        link.classList.add("active");
      });

    // When a color theme link is clicked
    document.querySelectorAll('[data-toggle="theme"]').forEach((el) => {
      el.addEventListener("click", (e) => {
        e.preventDefault();

        // Get element's data
        let themeName = el.dataset.theme;

        // Set this color theme link as active
        document.querySelectorAll('[data-toggle="theme"]').forEach((link) => {
          link.classList.remove("active");
        });

        document
          .querySelector(
            '[data-toggle="theme"][data-theme="' + themeName + '"]'
          )
          .classList.add("active");

        // Update color theme
        self._uiUpdateTheme(themeEl, themeName);

        // Update theme element
        themeEl = document.getElementById("css-theme");

        // If remember theme is enabled, save the new active color theme
        if (rememberTheme) {
          localStorage.setItem("dashmixColorTheme", themeName);
        }
      });
    });
  }

  /*
   * Updates the color theme
   *
   */
  _uiUpdateTheme(themeEl, themeName) {
    if (themeName === "default") {
      if (themeEl) {
        themeEl.parentNode.removeChild(themeEl);
      }
    } else {
      if (themeEl) {
        themeEl.setAttribute("href", themeName);
      } else {
        let themeLink = document.createElement("link");

        themeLink.id = "css-theme";
        themeLink.setAttribute("rel", "stylesheet");
        themeLink.setAttribute("href", themeName);

        document
          .getElementById("css-main")
          .insertAdjacentElement("afterend", themeLink);
      }
    }
  }

  /*
   * Layout API
   *
   */
  _uiApiLayout(mode = "init") {
    let self = this;

    // API with object literals
    let layoutAPI = {
      init: () => {
        let buttons = document.querySelectorAll('[data-toggle="layout"]');

        // Call layout API on button click
        if (buttons) {
          buttons.forEach((btn) => {
            btn.addEventListener("click", (e) => {
              self._uiApiLayout(btn.dataset.action);
            });
          });
        }

        // Prepend Page Overlay div if enabled (used when Side Overlay opens)
        if (self._lPage.classList.contains("enable-page-overlay")) {
          let pageOverlay = document.createElement("div");
          pageOverlay.id = "page-overlay";

          self._lPage.insertBefore(pageOverlay, self._lPage.firstChild);

          document
            .getElementById("page-overlay")
            .addEventListener("click", (e) => {
              self._uiApiLayout("side_overlay_close");
            });
        }
      },
      sidebar_pos_toggle: () => {
        self._lPage.classList.toggle("sidebar-r");
      },
      sidebar_pos_left: () => {
        self._lPage.classList.remove("sidebar-r");
      },
      sidebar_pos_right: () => {
        self._lPage.classList.add("sidebar-r");
      },
      sidebar_toggle: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.toggle("sidebar-o");
        } else {
          self._lPage.classList.toggle("sidebar-o-xs");
        }
      },
      sidebar_open: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.add("sidebar-o");
        } else {
          self._lPage.classList.add("sidebar-o-xs");
        }
      },
      sidebar_close: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.remove("sidebar-o");
        } else {
          self._lPage.classList.remove("sidebar-o-xs");
        }
      },
      sidebar_mini_toggle: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.toggle("sidebar-mini");
        }
      },
      sidebar_mini_on: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.add("sidebar-mini");
        }
      },
      sidebar_mini_off: () => {
        if (window.innerWidth > 991) {
          self._lPage.classList.remove("sidebar-mini");
        }
      },
      sidebar_style_toggle: () => {
        if (self._lPage.classList.contains("sidebar-dark")) {
          self._uiApiLayout("sidebar_style_light");
        } else {
          self._uiApiLayout("sidebar_style_dark");
        }
      },
      sidebar_style_dark: () => {
        self._lPage.classList.add("sidebar-dark");
      },
      sidebar_style_light: () => {
        self._lPage.classList.remove("sidebar-dark");
      },
      side_overlay_toggle: () => {
        if (self._lPage.classList.contains("side-overlay-o")) {
          self._uiApiLayout("side_overlay_close");
        } else {
          self._uiApiLayout("side_overlay_open");
        }
      },
      side_overlay_open: () => {
        // When ESCAPE key is hit close the side overlay
        document.addEventListener("keydown", (e) => {
          if (e.key === "Esc" || e.key === "Escape") {
            self._uiApiLayout("side_overlay_close");
          }
        });

        self._lPage.classList.add("side-overlay-o");
      },
      side_overlay_close: () => {
        self._lPage.classList.remove("side-overlay-o");
      },
      side_overlay_mode_hover_toggle: () => {
        self._lPage.classList.toggle("side-overlay-hover");
      },
      side_overlay_mode_hover_on: () => {
        self._lPage.classList.add("side-overlay-hover");
      },
      side_overlay_mode_hover_off: () => {
        self._lPage.classList.remove("side-overlay-hover");
      },
      header_glass_toggle: () => {
        self._lPage.classList.toggle("page-header-glass");
        self._uiHandleHeader();
      },
      header_glass_on: () => {
        self._lPage.classList.add("page-header-glass");
        self._uiHandleHeader();
      },
      header_glass_off: () => {
        self._lPage.classList.remove("page-header-glass");
        self._uiHandleHeader();
      },
      header_mode_toggle: () => {
        self._lPage.classList.toggle("page-header-fixed");
      },
      header_mode_static: () => {
        self._lPage.classList.remove("page-header-fixed");
      },
      header_mode_fixed: () => {
        self._lPage.classList.add("page-header-fixed");
      },
      header_style_toggle: () => {
        if (self._lPage.classList.contains("page-header-dark")) {
          self._uiApiLayout("header_style_light");
        } else {
          self._uiApiLayout("header_style_dark");
        }
      },
      header_style_dark: () => {
        self._lPage.classList.add("page-header-dark");
      },
      header_style_light: () => {
        self._lPage.classList.remove("page-header-dark");
      },
      header_search_on: () => {
        self._lHeaderSearch.classList.add("show");
        self._lHeaderSearchInput.focus();

        // When ESCAPE key is hit close the search section
        document.addEventListener("keydown", (e) => {
          if (e.key === "Esc" || e.key === "Escape") {
            self._uiApiLayout("header_search_off");
          }
        });
      },
      header_search_off: () => {
        self._lHeaderSearch.classList.remove("show");
        self._lHeaderSearchInput.blur();
      },
      header_loader_on: () => {
        self._lHeaderLoader.classList.add("show");
      },
      header_loader_off: () => {
        self._lHeaderLoader.classList.remove("show");
      },
      dark_mode_toggle: () => {
        if (self._lHtml.classList.contains("dark")) {
          self._uiApiLayout("dark_mode_off");
        } else {
          self._uiApiLayout("dark_mode_on");
        }
      },
      dark_mode_on: () => {
        this._uiHandleDarkMode("on");
      },
      dark_mode_off: () => {
        this._uiHandleDarkMode("off");
      },
      dark_mode_system: () => {
        this._uiHandleDarkMode("system");
      },
      content_layout_toggle: () => {
        if (self._lPage.classList.contains("main-content-boxed")) {
          self._uiApiLayout("content_layout_narrow");
        } else if (self._lPage.classList.contains("main-content-narrow")) {
          self._uiApiLayout("content_layout_full_width");
        } else {
          self._uiApiLayout("content_layout_boxed");
        }
      },
      content_layout_boxed: () => {
        self._lPage.classList.remove("main-content-narrow");
        self._lPage.classList.add("main-content-boxed");
      },
      content_layout_narrow: () => {
        self._lPage.classList.remove("main-content-boxed");
        self._lPage.classList.add("main-content-narrow");
      },
      content_layout_full_width: () => {
        self._lPage.classList.remove("main-content-boxed");
        self._lPage.classList.remove("main-content-narrow");
      },
    };

    // Call layout API
    if (layoutAPI[mode]) {
      layoutAPI[mode]();
    }
  }

  /*
   * Blocks API
   *
   */
  _uiApiBlocks(mode = "init", block = false) {
    let self = this;

    // Helper variables
    let elBlock, btnFullscreen, btnContentToggle;

    // Set default icons for fullscreen and content toggle buttons
    let iconBase = "si";
    let iconFullscreen = "si-size-fullscreen";
    let iconFullscreenActive = "si-size-actual";
    let iconContent = "si-arrow-up";
    let iconContentActive = "si-arrow-down";

    // API with object literals
    let blockAPI = {
      init: () => {
        // Auto add the default toggle icons to fullscreen and content toggle buttons
        document
          .querySelectorAll(
            '[data-toggle="block-option"][data-action="fullscreen_toggle"]'
          )
          .forEach((btn) => {
            btn.innerHTML =
              '<i class="' +
              iconBase +
              " " +
              (btn.closest(".block").classList.contains("block-mode-fullscreen")
                ? iconFullscreenActive
                : iconFullscreen) +
              '"></i>';
          });

        document
          .querySelectorAll(
            '[data-toggle="block-option"][data-action="content_toggle"]'
          )
          .forEach((btn) => {
            btn.innerHTML =
              '<i class="' +
              iconBase +
              " " +
              (btn.closest(".block").classList.contains("block-mode-hidden")
                ? iconContentActive
                : iconContent) +
              '"></i>';
          });

        // Call blocks API on option button click
        document
          .querySelectorAll('[data-toggle="block-option"]')
          .forEach((btn) => {
            btn.addEventListener("click", (e) => {
              this._uiApiBlocks(btn.dataset.action, btn.closest(".block"));
            });
          });
      },
      fullscreen_toggle: () => {
        elBlock.classList.remove("block-mode-pinned");
        elBlock.classList.toggle("block-mode-fullscreen");

        // Update block option icon
        if (btnFullscreen) {
          if (elBlock.classList.contains("block-mode-fullscreen")) {
            btnFullscreen &&
              btnFullscreen
                .querySelector("i")
                .classList.replace(iconFullscreen, iconFullscreenActive);
          } else {
            btnFullscreen &&
              btnFullscreen
                .querySelector("i")
                .classList.replace(iconFullscreenActive, iconFullscreen);
          }
        }
      },
      fullscreen_on: () => {
        elBlock.classList.remove("block-mode-pinned");
        elBlock.classList.add("block-mode-fullscreen");

        // Update block option icon
        btnFullscreen &&
          btnFullscreen
            .querySelector("i")
            .classList.replace(iconFullscreen, iconFullscreenActive);
      },
      fullscreen_off: () => {
        elBlock.classList.remove("block-mode-fullscreen");

        // Update block option icon
        btnFullscreen &&
          btnFullscreen
            .querySelector("i")
            .classList.replace(iconFullscreenActive, iconFullscreen);
      },
      content_toggle: () => {
        elBlock.classList.toggle("block-mode-hidden");

        // Update block option icon
        if (btnContentToggle) {
          if (elBlock.classList.contains("block-mode-hidden")) {
            btnContentToggle
              .querySelector("i")
              .classList.replace(iconContent, iconContentActive);
          } else {
            btnContentToggle
              .querySelector("i")
              .classList.replace(iconContentActive, iconContent);
          }
        }
      },
      content_hide: () => {
        elBlock.classList.add("block-mode-hidden");

        // Update block option icon
        btnContentToggle &&
          btnContentToggle
            .querySelector("i")
            .classList.replace(iconContent, iconContentActive);
      },
      content_show: () => {
        elBlock.classList.remove("block-mode-hidden");

        // Update block option icon
        btnContentToggle &&
          btnContentToggle
            .querySelector("i")
            .classList.replace(iconContentActive, iconContent);
      },
      state_toggle: () => {
        elBlock.classList.toggle("block-mode-loading");

        // Return block to normal state if the demostration mode is on in the refresh option button - data-action-mode="demo"
        if (
          elBlock.querySelector(
            '[data-toggle="block-option"][data-action="state_toggle"][data-action-mode="demo"]'
          )
        ) {
          setTimeout(() => {
            elBlock.classList.remove("block-mode-loading");
          }, 2000);
        }
      },
      state_loading: () => {
        elBlock.classList.add("block-mode-loading");
      },
      state_normal: () => {
        elBlock.classList.remove("block-mode-loading");
      },
      pinned_toggle: () => {
        elBlock.classList.remove("block-mode-fullscreen");
        elBlock.classList.toggle("block-mode-pinned");
      },
      pinned_on: () => {
        elBlock.classList.remove("block-mode-fullscreen");
        elBlock.classList.add("block-mode-pinned");
      },
      pinned_off: () => {
        elBlock.classList.remove("block-mode-pinned");
      },
      close: () => {
        elBlock.classList.add("d-none");
      },
      open: () => {
        elBlock.classList.remove("d-none");
      },
    };

    if (mode === "init") {
      // Call Block API
      blockAPI[mode]();
    } else {
      // Get block element
      elBlock =
        block instanceof Element ? block : document.querySelector(`${block}`);

      // If element exists, procceed with block functionality
      if (elBlock) {
        // Get block option buttons if exist (need them to update their icons)
        btnFullscreen = elBlock.querySelector(
          '[data-toggle="block-option"][data-action="fullscreen_toggle"]'
        );
        btnContentToggle = elBlock.querySelector(
          '[data-toggle="block-option"][data-action="content_toggle"]'
        );

        // Call Block API
        if (blockAPI[mode]) {
          blockAPI[mode]();
        }
      }
    }
  }

  /*
   ********************************************************************************************
   *
   * Helpers
   *
   *********************************************************************************************
   */

  /*
   * On DOM content loaded
   *
   */
  onLoad(fn) {
    if (document.readyState != "loading") {
      fn();
    } else {
      document.addEventListener("DOMContentLoaded", fn);
    }
  }

  /*
   ********************************************************************************************
   *
   * Create aliases for easier/quicker access to vital methods
   *
   *********************************************************************************************
   */

  /*
   * Init base functionality
   *
   */
  init() {
    this._uiInit();
  }

  /*
   * Layout API
   *
   */
  layout(mode) {
    this._uiApiLayout(mode);
  }

  /*
   * Blocks API
   *
   */
  block(mode, block) {
    this._uiApiBlocks(mode, block);
  }

  /*
   * Handle Page Loader
   *
   */
  loader(mode, colorClass) {
    this._uiHandlePageLoader(mode, colorClass);
  }

  /*
   * Run Helpers
   *
   */
  helpers(helpers, options = {}) {
    Helpers.run(helpers, options);
  }

  /*
   * Run Helpers on DOM content loaded
   *
   */
  helpersOnLoad(helpers, options = {}) {
    this.onLoad(() => Helpers.run(helpers, options));
  }
}
