{"version":3,"file":"index.cjs","sources":["../src/ElFinderPicker.ts","../src/helpers.ts"],"sourcesContent":["'use strict';\n\nimport './styles.css';\nimport type {\n  ElFinderPickerConfig,\n  ElFinderFile,\n  FilePickerCallback,\n  PickerMessage,\n  NavigateToFileMessage\n} from './types';\n\nclass ElFinderPicker {\n  private settings: { url: string; title: string };\n  private callback: FilePickerCallback | null;\n  private popup: HTMLElement | null;\n  private iframe: HTMLIFrameElement | null;\n  private _messageHandler: (event: MessageEvent<PickerMessage>) => void;\n\n  constructor(config: ElFinderPickerConfig = {}) {\n    this.settings = {\n      url: config.url || '',\n      title: config.title || 'File Manager',\n    };\n    this.callback = null;\n    this.popup = null;\n    this.iframe = null;\n    this._messageHandler = this._handleMessage.bind(this);\n  }\n\n  /**\n   * Handle postMessage from iframe\n   * @private\n   */\n  private _handleMessage(event: MessageEvent<PickerMessage>): void {\n    // Security check - verify message is from our iframe\n    if (!this.iframe || event.source !== this.iframe.contentWindow) return;\n\n    // Check if this is an elFinder file selection\n    if (event.data && event.data.action === 'FILE_SELECTED' && 'file' in event.data) {\n      this.onSelect(event.data.file);\n    }\n  }\n\n  /**\n   * Open the file picker\n   * @param cb - Callback function that receives file object\n   * @param v - Optional initial file path to navigate to\n   * @returns The popup element\n   */\n  open(cb: FilePickerCallback, v?: string): HTMLElement | null {\n    this.callback = cb;\n\n    if (this.popup) {\n      this.show();\n      // Send navigation message if value provided\n      if (v && this.iframe) {\n        this._sendNavigationMessage(v);\n      }\n    } else {\n      this.createPopup();\n      window.addEventListener('message', this._messageHandler);\n\n      // Send navigation message after iframe loads\n      if (v && this.iframe) {\n        this.iframe.addEventListener('load', () => {\n          // Small delay to ensure elFinder is initialized\n          setTimeout(() => this._sendNavigationMessage(v), 500);\n        }, { once: true });\n      }\n    }\n\n    return this.popup;\n  }\n\n  /**\n   * Send navigation message to iframe\n   * @private\n   */\n  private _sendNavigationMessage(path: string): void {\n    if (this.iframe && this.iframe.contentWindow) {\n      const message: NavigateToFileMessage = {\n        action: 'NAVIGATE_TO_FILE',\n        path: path\n      };\n      this.iframe.contentWindow.postMessage(message, '*');\n    }\n  }\n\n  /**\n   * Create the popup DOM structure\n   * @private\n   */\n  private createPopup(): void {\n    const popupHTML = `\n      <div class='elfinder-picker'>\n        <div class='elfinder-picker-backdrop'></div>\n        <div class='elfinder-picker-content'>\n          <h3 class='elfinder-picker-title'>${this.settings.title}</h3>\n          <div class='elfinder-picker-close'>&times;</div>\n          <iframe class='elfinder-picker-iframe' src='${this.settings.url}' allowfullscreen data-mode='select'></iframe>\n        </div>\n      </div>\n    `;\n\n    const tempDiv = document.createElement('div');\n    tempDiv.innerHTML = popupHTML.trim();\n    this.popup = tempDiv.firstChild as HTMLElement;\n\n    document.body.appendChild(this.popup);\n\n    // Store reference to iframe for postMessage communication\n    this.iframe = this.popup.querySelector('.elfinder-picker-iframe');\n\n    // Add event listeners\n    const closeBtn = this.popup.querySelector('.elfinder-picker-close');\n    const backdrop = this.popup.querySelector('.elfinder-picker-backdrop');\n\n    closeBtn?.addEventListener('click', () => this.close());\n    backdrop?.addEventListener('click', () => this.close());\n  }\n\n  /**\n   * Show the popup\n   * @private\n   */\n  private show(): void {\n    if (this.popup) {\n      this.popup.style.display = 'block';\n    }\n  }\n\n  /**\n   * Handle file selection from elFinder\n   * @param file - Raw file object from elFinder\n   */\n  private onSelect(file: ElFinderFile): void {\n    // URL normalization\n    let url = file.url;\n    const reg = /\\/[^/]+?\\/\\.\\.\\//;\n    while (url.match(reg)) {\n      url = url.replace(reg, '/');\n    }\n\n    // Remove double slashes (but preserve protocol://)\n    url = url.replace(/([^:]\\/)\\/+/g, '$1');\n\n    // Create normalized file object with clean data\n    const normalizedFile: ElFinderFile = {\n      url: url,\n      name: file.name,\n      mime: file.mime || 'application/octet-stream'\n    };\n\n    // Return raw file data - let the user decide how to handle it\n    if (this.callback) {\n      this.callback(normalizedFile);\n    }\n\n    this.close();\n  }\n\n  /**\n   * Close the popup\n   */\n  close(): void {\n    if (this.popup) {\n      this.popup.style.display = 'none';\n    }\n  }\n\n  /**\n   * Destroy the popup and clean up\n   */\n  destroy(): void {\n    // Remove message listener\n    window.removeEventListener('message', this._messageHandler);\n\n    if (this.popup && this.popup.parentNode) {\n      this.popup.parentNode.removeChild(this.popup);\n      this.popup = null;\n    }\n    this.iframe = null;\n    this.callback = null;\n  }\n}\n\nexport default ElFinderPicker;\n","'use strict';\n\nimport type {\n  ElFinderFile,\n  ElFinderInstance,\n  FilePickerCallbackOptions,\n  FileSelectedMessage,\n  NavigateToFileMessage\n} from './types';\n\n/**\n * Helper function for elFinder's getFileCallback\n * Use this directly in your elFinder configuration\n * Also sets up listener for navigation messages from parent\n *\n * @example\n * // In your elFinder page:\n * import { filePickerCallback } from 'elfinder-picker';\n *\n * $('#elfinder').elfinder({\n *   url: '/connector.php',\n *   getFileCallback: filePickerCallback\n * });\n */\nexport function filePickerCallback(\n  file: ElFinderFile,\n  fm?: ElFinderInstance,\n  options: FilePickerCallbackOptions = {}\n): void {\n  // Check if we're in an iframe (select mode)\n  if (!window.parent || window.parent === window) {\n    return; // Not in iframe, do nothing\n  }\n\n  const origin = options.origin || '*';\n\n  // Set up listener for navigation messages from parent (only once)\n  if (!(window as any)._isListenining) {\n    (window as any)._isListenining = true;\n    window.addEventListener('message', (event: MessageEvent<NavigateToFileMessage>) => {\n      // Handle NAVIGATE_TO_FILE message\n      if (event.data && event.data.action === 'NAVIGATE_TO_FILE' && event.data.path) {\n        if (fm && typeof fm.exec === 'function') {\n          // Use elFinder's exec method to open the file\n          fm.exec('open', event.data.path);\n        }\n      }\n    });\n  }\n\n  // Send file data to parent window\n  const message: FileSelectedMessage = {\n    action: 'FILE_SELECTED',\n    file: {\n      url: file.url,\n      name: file.name,\n      mime: file.mime || 'application/octet-stream'\n    }\n  };\n  window.parent.postMessage(message, origin);\n}\n\n/**\n * Create a configured file picker callback with custom options\n *\n * @param options - Configuration options\n * @returns Configured callback function\n * @example\n * import { createFilePickerCallback } from 'elfinder-picker';\n *\n * const callback = createFilePickerCallback({\n *   origin: 'https://yourdomain.com'\n * });\n *\n * $('#elfinder').elfinder({\n *   url: '/connector.php',\n *   getFileCallback: callback\n * });\n */\nexport function createFilePickerCallback(\n  options: FilePickerCallbackOptions = {}\n): (file: ElFinderFile, fm?: ElFinderInstance) => void {\n  return function(file: ElFinderFile, fm?: ElFinderInstance) {\n    filePickerCallback(file, fm, options);\n  };\n}\n"],"names":["ElFinderPicker","config","event","cb","v","path","message","popupHTML","tempDiv","closeBtn","backdrop","file","url","reg","normalizedFile","filePickerCallback","fm","options","origin","createFilePickerCallback"],"mappings":"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"}