utilikit-1.0.0/modules/utilikit_help/js/utilikit-help.js

modules/utilikit_help/js/utilikit-help.js
/**
 * @file
 * UtiliKit Help Page JavaScript - Interactive documentation functionality.
 *
 * Provides comprehensive interactive features for the UtiliKit help system
 * including responsive tab navigation, code copying, expandable sections,
 * performance monitoring, and educational enhancements. Creates an engaging
 * documentation experience with accessibility support and mobile optimization.
 */

(function(Drupal, once, drupalSettings) {
  'use strict';

  /**
   * UtiliKit Help Engine Behavior - Forces inline rendering for examples.
   *
   * Ensures all UtiliKit examples in the help documentation render using
   * inline mode regardless of global settings, providing immediate visual
   * feedback for educational purposes.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches inline rendering to UtiliKit elements in help context.
   */
  Drupal.behaviors.utilikitHelp = {
    attach: function(context, settings) {
      // Always use inline mode regardless of global settings
      const elements = once('utilikit-examples', '.utilikit', context);
      if (elements.length > 0) {
        // Force apply classes using inline engine
        if (typeof Drupal.utilikit !== 'undefined' && typeof Drupal.utilikit.applyClasses === 'function') {
          Drupal.utilikit.applyClasses(elements);
        }
      }
    }
  };
})(Drupal, once, drupalSettings);

(function(Drupal, once) {
  'use strict';

  /**
   * UtiliKit Help Engine Behavior - Secondary inline rendering handler.
   *
   * Provides redundant inline rendering functionality to ensure all
   * UtiliKit examples display correctly in the help documentation.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches additional inline rendering support.
   */
  Drupal.behaviors.utilikitHelpEngine = {
    attach: function(context, settings) {
      // Always use inline mode regardless of global settings
      const elements = once('utilikit-examples', '.utilikit', context);
      if (elements.length > 0) {
        // Force apply classes using inline engine
        if (typeof Drupal.utilikit !== 'undefined' && typeof Drupal.utilikit.applyClasses === 'function') {
          Drupal.utilikit.applyClasses(elements);
        }
      }
    }
  };

  /**
   * UtiliKit Help Page Main Behavior.
   *
   * Initializes the comprehensive help page functionality including
   * responsive tabs, interactive elements, performance monitoring,
   * and accessibility features.
   *
   * @type {Drupal~behavior}
   *
   * @prop {Drupal~behaviorAttach} attach
   *   Attaches all help page interactive functionality.
   */
  Drupal.behaviors.utilikitHelp = {
    attach: function(context, settings) {
      once('utilikit-help', '.utilikit-help-page', context).forEach(function(helpPage) {
        initializeHelpPage(helpPage);
      });
    }
  };

  /**
   * Initializes the complete help page functionality.
   *
   * Orchestrates the setup of all interactive features including responsive
   * tab navigation, accessibility support, performance monitoring, and
   * educational enhancements.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeHelpPage(helpPage) {
    const tabsWrapper = helpPage.querySelector('.vertical-tabs-wrapper');
    if (!tabsWrapper) return;

    // Initialize responsive tabs
    initializeResponsiveTabs(tabsWrapper);

    // Initialize tab switching
    initializeTabSwitching(tabsWrapper);

    // Initialize responsive features
    initializeResponsiveFeatures(helpPage);

    // Initialize interactive elements
    initializeInteractiveElements(helpPage);

    // Initialize performance monitoring
    initializePerformanceMonitoring(helpPage);
  }

  /**
   * Initializes responsive tab behavior for different screen sizes.
   *
   * Handles the transformation between vertical tabs (desktop) and
   * horizontal navigation (mobile) with smooth transitions and
   * proper event handling.
   *
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function initializeResponsiveTabs(tabsWrapper) {
    const breakpoint = parseInt(tabsWrapper.dataset.responsiveTabs) || 992;

    /**
     * Handles window resize events and applies appropriate tab layout.
     */
    function handleResize() {
      const windowWidth = window.innerWidth;

      if (windowWidth <= breakpoint) {
        // Mobile mode: add mobile classes and behavior
        tabsWrapper.classList.add('mobile-tabs');
        setupMobileNavigation(tabsWrapper);
      } else {
        // Desktop mode: remove mobile classes
        tabsWrapper.classList.remove('mobile-tabs', 'nav-open');
        removeMobileNavigation(tabsWrapper);
      }
    }

    // Initial setup
    handleResize();

    // Listen for resize events with debouncing
    let resizeTimeout;
    window.addEventListener('resize', function() {
      clearTimeout(resizeTimeout);
      resizeTimeout = setTimeout(handleResize, 150);
    });
  }

  /**
   * Sets up mobile navigation toggle functionality.
   *
   * Creates touch-friendly navigation for mobile devices with smooth
   * animations and proper event handling for tab menu opening/closing.
   *
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function setupMobileNavigation(tabsWrapper) {
    // Remove existing click handler
    tabsWrapper.removeEventListener('click', tabsWrapper._mobileToggleHandler);

    // Create new handler
    tabsWrapper._mobileToggleHandler = function(event) {
      // Check if click is on the pseudo-element area (navigation toggle)
      if (event.target === tabsWrapper && event.offsetY <= 60) {
        tabsWrapper.classList.toggle('nav-open');

        // Smooth scroll animation for nav opening
        if (tabsWrapper.classList.contains('nav-open')) {
          const nav = tabsWrapper.querySelector('.vertical-tabs-nav');
          if (nav) {
            nav.style.maxHeight = nav.scrollHeight + 'px';
            setTimeout(() => {
              nav.style.maxHeight = '';
            }, 300);
          }
        }
      }
    };

    // Add click handler for mobile toggle
    tabsWrapper.addEventListener('click', tabsWrapper._mobileToggleHandler);
  }

  /**
   * Removes mobile navigation setup and event handlers.
   *
   * Cleans up mobile-specific event listeners and functionality
   * when switching back to desktop layout.
   *
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function removeMobileNavigation(tabsWrapper) {
    if (tabsWrapper._mobileToggleHandler) {
      tabsWrapper.removeEventListener('click', tabsWrapper._mobileToggleHandler);
      delete tabsWrapper._mobileToggleHandler;
    }
  }

  /**
   * Initializes tab switching functionality with accessibility support.
   *
   * Sets up click and keyboard navigation for tabs, URL hash support
   * for direct linking, and proper ARIA attributes for screen readers.
   *
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function initializeTabSwitching(tabsWrapper) {
    const tabButtons = tabsWrapper.querySelectorAll('.tab-button');
    const tabContents = tabsWrapper.querySelectorAll('.tab-content');

    tabButtons.forEach(function(button) {
      button.addEventListener('click', function() {
        const targetTab = this.dataset.tab;
        switchToTab(targetTab, tabButtons, tabContents, tabsWrapper);
      });

      // Keyboard navigation
      button.addEventListener('keydown', function(event) {
        if (event.key === 'Enter' || event.key === ' ') {
          event.preventDefault();
          this.click();
        }
      });
    });

    // URL hash support for direct linking
    initializeHashNavigation(tabButtons, tabContents, tabsWrapper);
  }

  /**
   * Switches to a specific tab with proper state management.
   *
   * Updates button states, content visibility, URL hash, and handles
   * mobile navigation closing with smooth scrolling animations.
   *
   * @param {string} targetTab
   *   The ID of the tab to switch to.
   * @param {NodeList} tabButtons
   *   Collection of all tab button elements.
   * @param {NodeList} tabContents
   *   Collection of all tab content elements.
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function switchToTab(targetTab, tabButtons, tabContents, tabsWrapper) {
    // Update button states
    tabButtons.forEach(function(btn) {
      btn.classList.remove('active');
      btn.setAttribute('aria-selected', 'false');
    });

    // Update content states
    tabContents.forEach(function(content) {
      content.classList.remove('active');
    });

    // Activate target tab
    const activeButton = tabsWrapper.querySelector(`[data-tab="${targetTab}"]`);
    const activeContent = tabsWrapper.querySelector(`#${targetTab}`);

    if (activeButton && activeContent) {
      activeButton.classList.add('active');
      activeButton.setAttribute('aria-selected', 'true');
      activeContent.classList.add('active');

      // Update URL hash without scrolling
      updateUrlHash(targetTab);

      // Close mobile navigation after selection
      if (tabsWrapper.classList.contains('mobile-tabs')) {
        tabsWrapper.classList.remove('nav-open');
      }

      // Smooth scroll to content on mobile
      if (window.innerWidth <= 992) {
        setTimeout(function() {
          activeContent.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
          });
        }, 100);
      }

      // Trigger custom event for analytics or other listeners
      dispatchTabChangeEvent(targetTab, activeContent);
    }
  }

  /**
   * Initializes URL hash navigation for deep linking support.
   *
   * Handles initial page load with hash fragments and browser
   * back/forward navigation to maintain proper tab state.
   *
   * @param {NodeList} tabButtons
   *   Collection of all tab button elements.
   * @param {NodeList} tabContents
   *   Collection of all tab content elements.
   * @param {Element} tabsWrapper
   *   The tabs wrapper container element.
   */
  function initializeHashNavigation(tabButtons, tabContents, tabsWrapper) {
    // Handle initial hash
    const initialHash = window.location.hash.substring(1);
    if (initialHash && document.getElementById(initialHash)) {
      switchToTab(initialHash, tabButtons, tabContents, tabsWrapper);
    }

    // Handle hash changes (back/forward navigation)
    window.addEventListener('hashchange', function() {
      const hash = window.location.hash.substring(1);
      if (hash && document.getElementById(hash)) {
        switchToTab(hash, tabButtons, tabContents, tabsWrapper);
      }
    });
  }

  /**
   * Updates URL hash without triggering page scroll.
   *
   * Uses modern History API when available, falls back to direct
   * hash modification for older browsers.
   *
   * @param {string} tabId
   *   The tab ID to set in the URL hash.
   */
  function updateUrlHash(tabId) {
    if (history.replaceState) {
      history.replaceState(null, null, '#' + tabId);
    } else {
      window.location.hash = tabId;
    }
  }

  /**
   * Dispatches custom tab change event for analytics and integrations.
   *
   * Creates a custom event with tab details that can be consumed
   * by analytics systems or other JavaScript components.
   *
   * @param {string} tabId
   *   The ID of the activated tab.
   * @param {Element} tabContent
   *   The content element for the activated tab.
   */
  function dispatchTabChangeEvent(tabId, tabContent) {
    const event = new CustomEvent('utilikitTabChange', {
      detail: {
        tabId: tabId,
        tabContent: tabContent,
        timestamp: new Date().toISOString()
      }
    });
    document.dispatchEvent(event);
  }

  /**
   * Initializes responsive features for enhanced user experience.
   *
   * Sets up screen size indicators, smooth scrolling for internal
   * links, and responsive behavior monitoring.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeResponsiveFeatures(helpPage) {
    // Current screen size indicator for responsive demo
    const currentSizeSpan = helpPage.querySelector('#current-size');
    if (currentSizeSpan) {
      updateCurrentSize(currentSizeSpan);

      let sizeTimeout;
      window.addEventListener('resize', function() {
        clearTimeout(sizeTimeout);
        sizeTimeout = setTimeout(function() {
          updateCurrentSize(currentSizeSpan);
        }, 100);
      });
    }

    // Smooth scroll for internal links
    helpPage.addEventListener('click', function(event) {
      const link = event.target.closest('a[href^="#"]');
      if (link) {
        event.preventDefault();
        const target = document.querySelector(link.getAttribute('href'));
        if (target) {
          target.scrollIntoView({
            behavior: 'smooth',
            block: 'start'
          });
        }
      }
    });
  }

  /**
   * Updates the current screen size display with animation.
   *
   * Shows the current breakpoint category and animates the change
   * to provide visual feedback during responsive testing.
   *
   * @param {Element} element
   *   The element to update with screen size information.
   */
  function updateCurrentSize(element) {
    const width = window.innerWidth;
    let size = 'Mobile';

    if (width >= 1400) size = 'XXL (1400px+)';
    else if (width >= 1200) size = 'XL (1200px+)';
    else if (width >= 992) size = 'Large (992px+)';
    else if (width >= 768) size = 'Medium (768px+)';
    else if (width >= 576) size = 'Small (576px+)';
    else size = 'Mobile (0px+)';

    element.textContent = size;

    // Add a subtle animation
    element.style.transform = 'scale(1.1)';
    element.style.color = '#007bff';
    setTimeout(function() {
      element.style.transform = '';
      element.style.color = '';
    }, 200);
  }

  /**
   * Initializes all interactive elements in the help page.
   *
   * Orchestrates the setup of code copying, expandable sections,
   * interactive examples, and tooltip functionality.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeInteractiveElements(helpPage) {
    // Code copy functionality
    initializeCodeCopy(helpPage);

    // Expandable code sections
    initializeExpandableCode(helpPage);

    // Interactive examples
    initializeInteractiveExamples(helpPage);

    // Tooltip functionality for technical terms
    initializeTooltips(helpPage);
  }

  /**
   * Initializes copy-to-clipboard functionality for code blocks.
   *
   * Adds hover-revealed copy buttons to all code blocks with
   * modern Clipboard API support and legacy fallbacks.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeCodeCopy(helpPage) {
    const codeBlocks = helpPage.querySelectorAll('pre code');

    codeBlocks.forEach(function(codeBlock) {
      const pre = codeBlock.parentElement;

      // Create copy button
      const copyButton = document.createElement('button');
      copyButton.className = 'code-copy-btn';
      copyButton.innerHTML = 'Copy';
      copyButton.setAttribute('aria-label', 'Copy code to clipboard');

      // Position button
      pre.style.position = 'relative';
      copyButton.style.position = 'absolute';
      copyButton.style.bottom = '0';
      copyButton.style.right = '10px';
      copyButton.style.padding = '5px 10px';
      copyButton.style.background = '#007bff';
      copyButton.style.color = 'white';
      copyButton.style.border = 'none';
      copyButton.style.borderRadius = '4px';
      copyButton.style.cursor = 'pointer';
      copyButton.style.fontSize = '0.8rem';
      copyButton.style.opacity = '0';
      copyButton.style.transition = 'opacity 0.3s ease';

      pre.appendChild(copyButton);

      // Show button on hover
      pre.addEventListener('mouseenter', function() {
        copyButton.style.opacity = '1';
      });

      pre.addEventListener('mouseleave', function() {
        copyButton.style.opacity = '0';
      });

      // Copy functionality
      copyButton.addEventListener('click', function(event) {
        event.preventDefault();

        const textToCopy = codeBlock.textContent;

        if (navigator.clipboard) {
          navigator.clipboard.writeText(textToCopy).then(function() {
            showCopySuccess(copyButton);
          }).catch(function() {
            fallbackCopyTextToClipboard(textToCopy, copyButton);
          });
        } else {
          fallbackCopyTextToClipboard(textToCopy, copyButton);
        }
      });
    });
  }

  /**
   * Shows visual feedback for successful copy operations.
   *
   * Temporarily changes button appearance to indicate successful
   * clipboard operation with automatic restoration.
   *
   * @param {Element} button
   *   The copy button element to update.
   */
  function showCopySuccess(button) {
    const originalText = button.innerHTML;
    button.innerHTML = 'Copied!';
    button.style.background = '#28a745';

    setTimeout(function() {
      button.innerHTML = originalText;
      button.style.background = '#007bff';
    }, 2000);
  }

  /**
   * Fallback copy method for browsers without Clipboard API.
   *
   * Uses the legacy execCommand method with temporary textarea
   * for copying text to clipboard.
   *
   * @param {string} text
   *   The text content to copy to clipboard.
   * @param {Element} button
   *   The copy button element for feedback display.
   */
  function fallbackCopyTextToClipboard(text, button) {
    const textArea = document.createElement('textarea');
    textArea.value = text;
    textArea.style.position = 'fixed';
    textArea.style.left = '-999999px';
    textArea.style.top = '-999999px';
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    try {
      const successful = document.execCommand('copy');
      if (successful) {
        showCopySuccess(button);
      } else {
        showCopyError(button);
      }
    } catch (err) {
      showCopyError(button);
    }

    document.body.removeChild(textArea);
  }

  /**
   * Shows visual feedback for failed copy operations.
   *
   * Displays error state on copy button with automatic restoration
   * to original state after timeout.
   *
   * @param {Element} button
   *   The copy button element to update.
   */
  function showCopyError(button) {
    const originalText = button.innerHTML;
    button.innerHTML = 'Failed';
    button.style.background = '#dc3545';

    setTimeout(function() {
      button.innerHTML = originalText;
      button.style.background = '#007bff';
    }, 2000);
  }

  /**
   * Initializes expandable code sections with smooth animations.
   *
   * Adds smooth expand/collapse animations to HTML details elements
   * containing code examples for better user experience.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeExpandableCode(helpPage) {
    const details = helpPage.querySelectorAll('details.code-details');

    details.forEach(function(detail) {
      const summary = detail.querySelector('summary');

      summary.addEventListener('click', function() {
        // Add smooth animation
        if (detail.open) {
          // Closing
          setTimeout(function() {
            detail.style.maxHeight = detail.scrollHeight + 'px';
            requestAnimationFrame(function() {
              detail.style.maxHeight = summary.scrollHeight + 'px';
            });
          }, 0);
        } else {
          // Opening
          setTimeout(function() {
            detail.style.maxHeight = detail.scrollHeight + 'px';
            setTimeout(function() {
              detail.style.maxHeight = '';
            }, 300);
          }, 0);
        }
      });
    });
  }

  /**
   * Initializes interactive examples with hover effects and click actions.
   *
   * Adds visual feedback for example cards and click-to-copy
   * functionality for inline code elements.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeInteractiveExamples(helpPage) {
    // Add hover effects to example cards
    const exampleCards = helpPage.querySelectorAll('.example-card');

    exampleCards.forEach(function(card) {
      card.addEventListener('mouseenter', function() {
        const demo = this.querySelector('.example-demo');
        if (demo) {
          demo.style.transform = 'scale(1.02)';
          demo.style.transition = 'transform 0.3s ease';
        }
      });

      card.addEventListener('mouseleave', function() {
        const demo = this.querySelector('.example-demo');
        if (demo) {
          demo.style.transform = '';
        }
      });
    });

    // Add click-to-highlight functionality for code examples
    const inlineCode = helpPage.querySelectorAll('code:not(pre code)');
    inlineCode.forEach(function(code) {
      code.addEventListener('click', function() {
        // Highlight effect
        this.style.background = '#ffeb3b';
        this.style.transition = 'background 0.3s ease';

        setTimeout(() => {
          this.style.background = '';
        }, 1000);

        // Copy to clipboard
        if (navigator.clipboard) {
          navigator.clipboard.writeText(this.textContent);
        }
      });

      // Add cursor pointer to indicate it's clickable
      code.style.cursor = 'pointer';
      code.title = 'Click to copy';
    });
  }

  /**
   * Initializes tooltips for technical terms throughout the help page.
   *
   * Automatically identifies technical terms and adds hover tooltips
   * with definitions to improve educational value.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializeTooltips(helpPage) {
    const technicalTerms = {
      'breakpoint': 'A specific screen width where the layout changes to accommodate different device sizes.',
      'utility class': 'A CSS class that applies a single, specific style property.',
      'responsive design': 'Design approach that makes web pages render well on different devices and screen sizes.',
      'CSS optimization': 'The process of reducing CSS file size and improving performance.',
      'cache busting': 'Technique to force browsers to download new versions of files instead of using cached versions.'
    };

    Object.keys(technicalTerms).forEach(function(term) {
      const regex = new RegExp(`\\b${term}\\b`, 'gi');
      const walker = document.createTreeWalker(
        helpPage,
        NodeFilter.SHOW_TEXT,
        null,
        false
      );

      const textNodes = [];
      let node;
      while (node = walker.nextNode()) {
        if (node.parentElement && !node.parentElement.closest('code, pre')) {
          textNodes.push(node);
        }
      }

      textNodes.forEach(function(textNode) {
        if (regex.test(textNode.textContent)) {
          const span = document.createElement('span');
          span.innerHTML = textNode.textContent.replace(regex, function(match) {
            return `<abbr class="help-tooltip" title="${technicalTerms[term.toLowerCase()]}">${match}</abbr>`;
          });
          textNode.parentNode.replaceChild(span, textNode);
        }
      });
    });

    // Style tooltips
    const style = document.createElement('style');
    style.textContent = `
      .help-tooltip {
        border-bottom: 1px dotted #007bff;
        cursor: help;
        text-decoration: none;
      }
      .help-tooltip:hover {
        color: #007bff;
      }
    `;
    document.head.appendChild(style);
  }

  /**
   * Initializes performance monitoring and analytics tracking.
   *
   * Sets up tab interaction tracking, scroll depth monitoring,
   * and page load performance measurement for optimization insights.
   *
   * @param {Element} helpPage
   *   The main help page container element.
   */
  function initializePerformanceMonitoring(helpPage) {
    // Track tab interactions for analytics
    document.addEventListener('utilikitTabChange', function(event) {
      // This could be connected to analytics systems
      if (window.gtag) {
        gtag('event', 'tab_change', {
          'tab_id': event.detail.tabId,
          'timestamp': event.detail.timestamp
        });
      }
    });

    // Monitor scroll depth for engagement tracking
    let maxScrollDepth = 0;
    const trackScrollDepth = throttle(function() {
      const scrollPercent = Math.round(
        (window.scrollY / (document.body.scrollHeight - window.innerHeight)) * 100
      );

      if (scrollPercent > maxScrollDepth) {
        maxScrollDepth = scrollPercent;

        // Track milestones
        if ([25, 50, 75, 100].includes(maxScrollDepth)) {
          if (window.gtag) {
            gtag('event', 'scroll_depth', {
              'percent': maxScrollDepth,
              'page': 'utilikit_help'
            });
          }
        }
      }
    }, 1000);

    window.addEventListener('scroll', trackScrollDepth);

    // Performance timing
    if (window.performance && window.performance.timing) {
      window.addEventListener('load', function() {
        setTimeout(function() {
          const loadTime = window.performance.timing.loadEventEnd - window.performance.timing.navigationStart;
          console.log('UtiliKit Help page load time:', loadTime + 'ms');
        }, 0);
      });
    }
  }

  /**
   * Throttles function execution for performance optimization.
   *
   * Limits the rate at which a function can fire to improve
   * performance during high-frequency events like scrolling.
   *
   * @param {Function} func
   *   The function to throttle.
   * @param {number} limit
   *   The minimum time between function executions in milliseconds.
   *
   * @returns {Function}
   *   The throttled function.
   */
  function throttle(func, limit) {
    let inThrottle;
    return function() {
      const args = arguments;
      const context = this;
      if (!inThrottle) {
        func.apply(context, args);
        inThrottle = true;
        setTimeout(() => inThrottle = false, limit);
      }
    };
  }

  /**
   * Developer console utilities and easter eggs.
   *
   * Provides helpful console messages and utility functions for
   * developers exploring the help system implementation.
   */
  if (window.console && console.log) {
    const styles = [
      'color: #007bff',
      'font-size: 16px',
      'font-weight: bold'
    ].join(';');

    console.log('%cWelcome to UtiliKit Help!', styles);
    console.log('%cTip: Try typing utilikitHelp.getTips() in the console!', 'color: #28a745');

    // Add some console utilities for fun
    window.utilikitHelp = {
      /**
       * Returns helpful tips for using UtiliKit.
       *
       * @returns {string[]} Array of helpful tips.
       */
      getTips: function() {
        return [
          'Use Developer Mode to see what UtiliKit is doing behind the scenes',
          'The Playground is perfect for testing ideas before implementing',
          'Always test responsive designs on real devices',
          'Static mode is great for production performance',
          'Check the browser console for helpful debug information'
        ];
      },

      /**
       * Returns version information.
       *
       * @returns {string} Version string with humorous message.
       */
      getVersion: function() {
        return 'UtiliKit Help v1.0 - Making documentation fun since 2025!';
      },

      /**
       * Triggers a fun visual effect for developers.
       *
       * @returns {string} Success message for the easter egg.
       */
      surprise: function() {
        document.body.style.animation = 'rainbow 2s ease-in-out';
        const style = document.createElement('style');
        style.textContent = `
          @keyframes rainbow {
            0% { filter: hue-rotate(0deg); }
            50% { filter: hue-rotate(180deg); }
            100% { filter: hue-rotate(360deg); }
          }
        `;
        document.head.appendChild(style);

        setTimeout(function() {
          document.body.style.animation = '';
          document.head.removeChild(style);
        }, 2000);

        return 'Surprise! You found the dev mode!';
      }
    };
  }

})(Drupal, once, drupalSettings);

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc