<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;">import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = ["container"]
  static values = {
    formId: String,
    firstVisit: { type: Boolean, default: true }
  }
  
  // Event handler bindings
  constructor(...args) {
    super(...args)
    // Bind all event handlers to maintain consistent references
    this.boundHandlers = {
      onModalShow: this.onModalShow.bind(this),
      handleModalFormSuccess: this.handleModalFormSuccess.bind(this),
      handleBeforeFetchResponse: this.handleBeforeFetchResponse.bind(this),
      handleModalHidden: this.handleModalHidden.bind(this),
      handleTurboRender: this.handleTurboRender.bind(this),
      handleTurboFrameRender: this.handleTurboFrameRender.bind(this),
      handleBeforeStreamRender: this.handleBeforeStreamRender.bind(this),
      handleStreamRender: this.handleStreamRender.bind(this),
      handleGlobalFormSubmit: this.handleGlobalFormSubmit.bind(this),
      handleEmptyFormButtonClick: this.handleEmptyFormButtonClick.bind(this),
      handleAdderClick: this.handleAdderClick.bind(this)
    }
  }

  connect() {
    console.log("Question adder controller connected")
    this.setupHoverAreas()
    this.setupModalListeners()
    this.setupGlobalListeners()
  }
  
  // Set up all modal-related event listeners
  setupModalListeners() {
    const modalElement = document.getElementById('newQuestionModal')
    if (!modalElement) return
    
    // Add event listeners for modal events
    modalElement.addEventListener('show.bs.modal', this.boundHandlers.onModalShow)
    modalElement.addEventListener('turbo:submit-success', this.boundHandlers.handleModalFormSuccess)
    modalElement.addEventListener('turbo:before-fetch-response', this.boundHandlers.handleBeforeFetchResponse)
    modalElement.addEventListener('hidden.bs.modal', this.boundHandlers.handleModalHidden)
  }
  
  // Set up all global document-level event listeners
  setupGlobalListeners() {
    // Listen for Turbo events to ensure adder areas are properly set up after navigation
    document.addEventListener('turbo:render', this.boundHandlers.handleTurboRender)
    document.addEventListener('turbo:frame-render', this.boundHandlers.handleTurboFrameRender)
    document.addEventListener('turbo:before-stream-render', this.boundHandlers.handleBeforeStreamRender)
    document.addEventListener('turbo:stream-render', this.boundHandlers.handleStreamRender)
    
    // Listen for turbo:submit-end on the document to catch all form submissions
    document.addEventListener('turbo:submit-end', this.boundHandlers.handleGlobalFormSubmit)
  }
  
  // Helper method to schedule adder reinitialization with a delay
  scheduleReinitialize(delay = 200) {
    setTimeout(() =&gt; {
      this.reinitializeAdders()
    }, delay)
  }
  
  // Turbo render event handlers
  handleTurboRender() {
    console.log("Turbo render detected, updating adder positions")
    this.scheduleReinitialize()
  }
  
  handleTurboFrameRender(event) {
    console.log("Turbo frame render detected", event.target.id)
    
    // Skip reinitialization for certain frame types
    const skipFrameIds = ['modal_new_question']
    if (event.target.id &amp;&amp; (
        skipFrameIds.includes(event.target.id) || 
        event.target.id.endsWith('-options'))) {
      console.log(`Skipping reinitialization for ${event.target.id} frame render`)
      return
    }
    
    this.scheduleReinitialize()
  }
  
  handleBeforeStreamRender(event) {
    console.log("Before stream render detected", event.detail.newStream)
    // We'll reinitialize after the stream is rendered
  }
  
  handleStreamRender(event) {
    console.log("Stream render detected", event.target)
    this.scheduleReinitialize(300)
  }
  
  handleGlobalFormSubmit(event) {
    // Check if this is a question form submission
    const form = event.target
    if (form &amp;&amp; form.action &amp;&amp; form.action.includes('/questions')) {
      console.log("Question form submitted", event.detail)
      
      if (event.detail.success) {
        // Wait for the DOM to update
        setTimeout(() =&gt; {
          this.reinitializeAdders()
        }, 500)
      }
    }
  }
  
  disconnect() {
    this.removeModalListeners()
    this.removeGlobalListeners()
    this.removeAdderClickListeners()
  }
  
  // Remove all modal-related event listeners
  removeModalListeners() {
    const modalElement = document.getElementById('newQuestionModal')
    if (!modalElement) return
    
    modalElement.removeEventListener('show.bs.modal', this.boundHandlers.onModalShow)
    modalElement.removeEventListener('turbo:submit-end', this.handleFormSubmit)
    modalElement.removeEventListener('turbo:submit-success', this.boundHandlers.handleModalFormSuccess)
    modalElement.removeEventListener('turbo:before-fetch-response', this.boundHandlers.handleBeforeFetchResponse)
    modalElement.removeEventListener('hidden.bs.modal', this.boundHandlers.handleModalHidden)
  }
  
  // Remove all global document-level event listeners
  removeGlobalListeners() {
    document.removeEventListener('turbo:render', this.boundHandlers.handleTurboRender)
    document.removeEventListener('turbo:frame-render', this.boundHandlers.handleTurboFrameRender)
    document.removeEventListener('turbo:before-stream-render', this.boundHandlers.handleBeforeStreamRender)
    document.removeEventListener('turbo:stream-render', this.boundHandlers.handleStreamRender)
    document.removeEventListener('turbo:submit-end', this.boundHandlers.handleGlobalFormSubmit)
  }
  
  // Remove click handlers from all adder areas
  removeAdderClickListeners() {
    document.querySelectorAll('.question-adder-hover-area').forEach(area =&gt; {
      area.removeEventListener('click', this.boundHandlers.handleAdderClick)
    })
  }
  
  
  onModalShow(event) {
    // Get the button that triggered the modal
    const button = event.relatedTarget
    if (!button) return
    
    // Get the position from the button's data attribute
    const position = button.getAttribute('data-position')
    if (!position) return
    
    this.setupModalForNewQuestion(button, position)
  }
  
  // Set up the modal for a new question at the specified position
  setupModalForNewQuestion(triggerElement, position) {
    // Update the modal's hidden position field
    const positionField = document.getElementById('modal_position_field')
    if (positionField) {
      positionField.value = position
    }
    
    // Get the modal element
    const modal = document.getElementById('newQuestionModal')
    if (!modal) return
    
    // Reset all flags when showing the modal
    modal.dataset.insertPosition = position
    modal.dataset.shouldClose = 'false'
    modal.dataset.hadSuccessfulSubmission = 'false'
    
    // Ensure modal doesn't have aria-hidden attribute when shown
    modal.removeAttribute('aria-hidden')
    
    // Check if this was triggered by the empty form button
    const isEmptyFormButton = triggerElement.classList.contains('question-adder-button-empty')
    modal.dataset.triggeredByEmptyButton = isEmptyFormButton ? 'true' : 'false'
    
    if (isEmptyFormButton) {
      console.log("Modal triggered by empty form button, will stay open after submission")
    }
    
    // Check if we're in reordering mode
    const isReorderingMode = document.getElementById('questions')?.classList.contains('reordering-mode') || false
    
    // Load the new question form via Turbo
    const modalFrame = document.getElementById('modal_new_question')
    if (modalFrame) {
      modalFrame.src = `/collect/admin/forms/${this.formIdValue}/questions/new?position=${position}&amp;reordering=${isReorderingMode ? 'true' : 'false'}`
    }
    
    // Add event listener to handle form submission via Turbo
    modal.removeEventListener('turbo:submit-end', this.handleFormSubmit)
    modal.addEventListener('turbo:submit-end', this.handleFormSubmit, { once: true })
  }
  
  // Handle form submission as a separate method to avoid closure issues
  handleFormSubmit = (e) =&gt; {
    // Check if the form submission was successful
    if (e.detail.success) {
      // Get the modal and mark it for closing
      const modal = document.getElementById('newQuestionModal')
      modal.dataset.hadSuccessfulSubmission = 'true'
      
      // Get the position from the modal's data attribute
      const position = modal.dataset.insertPosition
      console.log(`Question added at position: ${position}`)
      
      // Close the modal
      const bsModal = bootstrap.Modal.getInstance(modal)
      if (bsModal) {
        bsModal.hide()
      }
      
      // Dispatch a custom event to notify other controllers
      this.element.dispatchEvent(new CustomEvent('questionAdded', {
        bubbles: true,
        detail: { position: position }
      }))
    }
  }
  
  // Centralized method to determine if modal should close and close it if needed
  closeModalIfNeeded(event, reason) {
    const modal = document.getElementById('newQuestionModal')
    if (!modal) return false
    
  
    // If we get here, we should close the modal
    console.log(`Closing modal (${reason})`)
    
    // Flag that we've had a successful submission
    modal.dataset.hadSuccessfulSubmission = 'true'
    
    // Remove aria-hidden before hiding to prevent accessibility errors
    modal.removeAttribute('aria-hidden')
    
    // Close the modal
    const bsModal = bootstrap.Modal.getInstance(modal)
    if (bsModal) {
      bsModal.hide()
    }
    
    return true
  }
  
  // Handle successful form submissions in the modal
  handleModalFormSuccess(event) {
    console.log("Modal form submitted successfully", event)
    
    // Get the position from the modal's data attribute
    const modal = document.getElementById('newQuestionModal')
    const position = modal.dataset.insertPosition
    console.log(`Question added at position: ${position}`)
    
    // Flag that we've had a successful submission
    modal.dataset.hadSuccessfulSubmission = 'true'
    // Try to close the modal, if it shouldn't close, handle accordingly
    if (!this.closeModalIfNeeded(event, 'form-success')) {
      // If the modal should stay open because it was triggered by the empty form button,
      // reset the form for a new submission
      if (modal.dataset.triggeredByEmptyButton === 'true') {
        // Reset the form by reloading the new question frame
        const modalFrame = document.getElementById('modal_new_question')
        if (modalFrame) {
          modalFrame.src = `/collect/admin/forms/${this.formIdValue}/questions/new?position=${position}&amp;reordering=false`
        }
      }
    }
  }
  
  // Handle before fetch response to detect successful form submissions
  handleBeforeFetchResponse(event) {
    const response = event.detail.fetchResponse
    
    // Check if this is a successful response from a question creation
    if (response.succeeded &amp;&amp; response.response.url.includes('/questions') &amp;&amp; 
        (response.response.status === 200 || response.response.status === 201)) {
      
      console.log("Detected successful question creation response")
      
      // Use the centralized method to determine if we should close the modal
      this.closeModalIfNeeded(event, 'before-fetch-response')
    }
  }
  
  // Handle modal hidden event
  handleModalHidden(event) {
    const modal = event.target
    
    console.log("Modal hidden event triggered", {
      hadSuccessfulSubmission: modal.dataset.hadSuccessfulSubmission,
      shouldClose: modal.dataset.shouldClose,
      triggeredByEmptyButton: modal.dataset.triggeredByEmptyButton
    })
    
    // Check if we had a successful submission
    if (modal.dataset.hadSuccessfulSubmission === 'true' || modal.dataset.shouldClose === 'true') {
      console.log("Modal hidden after successful submission, reinitializing adders")
      
      this.resetModalFlags(modal)
      this.checkAndUpdateEmptyState()
      this.scheduleReinitialize(500)
    }
  }
  
  // Reset modal flags after handling
  resetModalFlags(modal) {
    // Clear the flags
    modal.dataset.hadSuccessfulSubmission = 'false'
    modal.dataset.shouldClose = 'false'
    
    // Only reset triggeredByEmptyButton if it wasn't set to true
    // This ensures we maintain the state for empty form button clicks
    if (modal.dataset.triggeredByEmptyButton !== 'true') {
      modal.dataset.triggeredByEmptyButton = 'false'
    }
    
    // Ensure modal doesn't have aria-hidden attribute when closed
    modal.removeAttribute('aria-hidden')
  }
  
  // Check if we need to remove the empty state
  checkAndUpdateEmptyState() {
    const questions = this.containerTarget.querySelectorAll('.sortable-item')
    if (questions.length &gt; 0 &amp;&amp; this.containerTarget.classList.contains('empty-questions')) {
      this.containerTarget.classList.remove('empty-questions')
      
      // Remove the empty state message
      const emptyArea = this.containerTarget.querySelector('#question-adder-hover-area-0')
      if (emptyArea) {
        emptyArea.remove()
      }
    }
  }
  
  // Handle click on the empty form button
  handleEmptyFormButtonClick(event) {
    // Mark the modal as triggered by the empty form button
    const modalElement = document.getElementById('newQuestionModal')
    if (modalElement) {
      // Set the flag explicitly
      modalElement.dataset.triggeredByEmptyButton = 'true'
      
      // Reset other flags to ensure they don't interfere
      modalElement.dataset.shouldClose = 'false'
      modalElement.dataset.hadSuccessfulSubmission = 'false'
      
      // Set the position in the hidden field
      const positionField = document.getElementById('modal_position_field')
      if (positionField) {
        positionField.value = '0'
      }
      
      // Update the modal's data attribute
      modalElement.dataset.insertPosition = '0'
      
      console.log("Empty form button clicked, modal will stay open after submission")
    }
  }
  
  // Update the position attributes of all adder areas to match their actual position in the DOM
  updateAdderPositions() {
    console.log("Updating adder positions")
    
    // Get all sortable items (questions)
    const questionItems = this.element.querySelectorAll('.sortable-item')
    
    // Get all adder areas
    const adderAreas = this.element.querySelectorAll('.question-adder-hover-area')
    
    // Update the first adder (position 0)
    this.updateAdderArea('#question-adder-hover-area-0', 0)
    
    // Then update all other adders that follow questions
    questionItems.forEach((item, index) =&gt; {
      // Find the adder that follows this question
      const nextAdder = item.nextElementSibling
      if (nextAdder &amp;&amp; nextAdder.classList.contains('question-adder-hover-area')) {
        this.updateAdderArea(nextAdder, index + 1)
      }
    })
    
    console.log(`Updated ${adderAreas.length} adder areas`)
  }
  
  // Update a single adder area with the correct position and event handlers
  updateAdderArea(adderOrSelector, position) {
    const adder = typeof adderOrSelector === 'string' 
      ? this.element.querySelector(adderOrSelector)
      : adderOrSelector
      
    if (!adder) return
    
    adder.dataset.position = position
    adder.id = `question-adder-hover-area-${position}`
    
    // Update the click handler
    adder.removeEventListener('click', this.boundHandlers.handleAdderClick)
    adder.addEventListener('click', this.boundHandlers.handleAdderClick)
  }
  
  // Completely reinitialize all adder areas after Turbo updates
  reinitializeAdders() {
    console.log("Reinitializing all adder areas")
    
    this.checkAndUpdateEmptyState()
    this.removeAdderClickListeners()
    this.setupHoverAreas()
    this.updateAdderPositions()
    
    console.log("Adder areas reinitialized")
  }
  
  // Create a standard first hover area element
  createFirstHoverArea() {
    const firstHoverArea = document.createElement('div')
    firstHoverArea.className = 'question-adder-hover-area w-100 first-hover-area'
    firstHoverArea.dataset.position = '0'
    firstHoverArea.id = 'question-adder-hover-area-0'
    
    firstHoverArea.innerHTML = `
      &lt;div class="w-100 h-100 position-relative"&gt;
        &lt;div class="question-adder-line"&gt;&lt;/div&gt;
        &lt;span class="question-adder-text"&gt;
          &lt;i class="bi bi-plus-circle me-1"&gt;&lt;/i&gt;AÃ±adir pregunta
        &lt;/span&gt;
      &lt;/div&gt;
    `
    
    return firstHoverArea
  }

  setupHoverAreas() {
    // Get all question items
    const questions = this.containerTarget.querySelectorAll('.sortable-item')
    
    if (questions.length === 0) {
      this.setupEmptyFormState()
    } else {
      this.setupNonEmptyFormState(questions)
    }
    
    // Add click handler to all adder areas
    document.querySelectorAll('.question-adder-hover-area').forEach(area =&gt; {
      // Remove existing click handlers to prevent duplicates
      area.removeEventListener('click', this.boundHandlers.handleAdderClick)
      
      // Add click handler
      area.addEventListener('click', this.boundHandlers.handleAdderClick)
    })
  }
  
  // Setup for empty forms
  setupEmptyFormState() {
    this.containerTarget.classList.add('empty-questions')
    
    // Add a first hover area for empty forms if it doesn't exist
    if (!this.containerTarget.querySelector('#question-adder-hover-area-0')) {
      const emptyHoverArea = document.createElement('div')
      emptyHoverArea.className = 'question-adder-hover-area w-100 first-hover-area'
      emptyHoverArea.dataset.position = '0'
      emptyHoverArea.id = 'question-adder-hover-area-0'
      
      emptyHoverArea.innerHTML = `
        &lt;div class="w-100 text-center py-5"&gt;
          &lt;div class="empty-form-message mb-5"&gt;
            &lt;div class="mb-4"&gt;
              &lt;i class="bi bi-clipboard-plus text-primary" style="font-size: 4rem;"&gt;&lt;/i&gt;
            &lt;/div&gt;
            &lt;h4 class="fw-light"&gt;Este formulario no tiene preguntas todavÃ­a&lt;/h4&gt;
          &lt;/div&gt;
          &lt;button type="button"
             class="btn btn-primary btn-lg question-adder-button-empty shadow-sm px-4 py-3"
             data-bs-toggle="modal" 
             data-bs-target="#newQuestionModal" 
             data-position="0"
             data-action="click-&gt;question-adder#handleEmptyFormButtonClick"&gt;
            &lt;i class="bi bi-plus-circle me-2"&gt;&lt;/i&gt;AÃ±adir primera pregunta
          &lt;/button&gt;
        &lt;/div&gt;
      `
      
      this.containerTarget.appendChild(emptyHoverArea)
    }
  }
  
  // Setup for non-empty forms
  setupNonEmptyFormState(questions) {
    this.containerTarget.classList.remove('empty-questions')
    
    // Make sure we have a first hover area
    if (!this.containerTarget.querySelector('#question-adder-hover-area-0')) {
      const firstHoverArea = this.createFirstHoverArea()
      const firstQuestion = questions[0]
      if (firstQuestion) {
        firstQuestion.before(firstHoverArea)
      }
    }
  }
  
  handleAdderClick = (event) =&gt; {
    // Prevent default to ensure no unexpected behavior
    event.preventDefault()
    
    // Only trigger if clicking the area itself, not the button
    // (the button already has its own modal trigger)
    if (!event.target.closest('.question-adder-button') &amp;&amp; 
        !event.target.closest('.question-adder-button-empty')) {
      const area = event.currentTarget
      const position = area.dataset.position
      
      console.log(`Clicked adder area with position: ${position}, id: ${area.id}`)
      
      // Get or create the modal
      let modalElement = document.getElementById('newQuestionModal')
      let modal = bootstrap.Modal.getInstance(modalElement)
      if (!modal) {
        modal = new bootstrap.Modal(modalElement)
      }
      
      // Setup the modal with this area as the trigger
      this.setupModalForNewQuestion(area, position)
      
      // Show the modal
      modal.show()
    }
  }
};
</pre></body></html>