/**
 * Content Script - Bundled Version
 * 
 * This is a bundled version that doesn't use ES6 modules.
 * Chrome MV3 content scripts don't support ES modules.
 */

console.log('[Filevine Plugin] Content script loaded');

// Inline constants (from src/lib/constants.js)
const MESSAGE_TYPES = {
  AUTH_STATE_CHANGED: 'auth_state_changed',
  AUTH_DETECTED: 'auth_detected',
  AUTH_EXPIRED: 'auth_expired',
  GET_AUTH_STATE: 'get_auth_state',
  API_REQUEST: 'api_request',
  API_RESPONSE: 'api_response',
  DOWNLOAD_FILE: 'download_file',
  DOWNLOAD_PROGRESS: 'download_progress',
  DOWNLOAD_COMPLETE: 'download_complete',
  DOWNLOAD_ERROR: 'download_error',
  GET_PROJECTS: 'get_projects',
  GET_PROJECT_DOCS: 'get_project_docs',
  OPEN_VIEWER: 'open_viewer',
};

// TokenDetector class (from src/content/detector.js)
class TokenDetector {
  constructor() {
    this.observers = [];
    this.lastUrl = window.location.href;
  }

  watchForChanges(callback) {
    this.setupUrlWatcher(callback);
    this.setupDomObserver(callback);
    this.setupStorageWatcher(callback);
  }

  setupUrlWatcher(callback) {
    const originalPushState = history.pushState;
    const originalReplaceState = history.replaceState;

    history.pushState = (...args) => {
      originalPushState.apply(history, args);
      this.onUrlChange(callback);
    };

    history.replaceState = (...args) => {
      originalReplaceState.apply(history, args);
      this.onUrlChange(callback);
    };

    window.addEventListener('popstate', () => {
      this.onUrlChange(callback);
    });
  }

  onUrlChange(callback) {
    const currentUrl = window.location.href;
    if (currentUrl !== this.lastUrl) {
      this.lastUrl = currentUrl;
      clearTimeout(this.urlChangeTimeout);
      this.urlChangeTimeout = setTimeout(() => {
        callback('url_change');
      }, 500);
    }
  }

  setupDomObserver(callback) {
    const observer = new MutationObserver((mutations) => {
      for (const mutation of mutations) {
        if (this.isAuthRelatedMutation(mutation)) {
          clearTimeout(this.domChangeTimeout);
          this.domChangeTimeout = setTimeout(() => {
            callback('dom_change');
          }, 1000);
          break;
        }
      }
    });

    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: false
    });

    this.observers.push(observer);
  }

  isAuthRelatedMutation(mutation) {
    const authPatterns = ['login', 'logout', 'auth', 'session', 'user-menu', 'profile'];
    for (const node of mutation.addedNodes) {
      if (node.nodeType !== Node.ELEMENT_NODE) continue;
      const html = node.outerHTML?.toLowerCase() || '';
      // className can be a DOMTokenList or string, handle both
      const classes = typeof node.className === 'string' 
        ? node.className.toLowerCase() 
        : (node.className?.toString() || '');
      const id = node.id?.toLowerCase() || '';
      for (const pattern of authPatterns) {
        if (html.includes(pattern) || classes.includes(pattern) || id.includes(pattern)) {
          return true;
        }
      }
    }
    return false;
  }

  setupStorageWatcher(callback) {
    window.addEventListener('storage', (event) => {
      const authKeys = ['access_token', 'fv_auth', 'oidc.user', 'auth', 'token'];
      if (authKeys.some(key => event.key?.includes(key))) {
        callback('storage_change');
      }
    });
  }

  isLoggedIn() {
    const indicators = [
      document.querySelector('[class*="user-menu"]'),
      document.querySelector('[class*="profile"]'),
      document.querySelector('[data-testid="user-avatar"]'),
      document.querySelector('[class*="project-list"]'),
      document.querySelector('[class*="app-container"]'),
      document.querySelector('[class*="main-content"]')
    ];
    const loginIndicators = [
      document.querySelector('form[action*="login"]'),
      document.querySelector('input[type="password"]'),
      document.querySelector('[class*="login-form"]')
    ];
    const hasAppIndicators = indicators.some(el => el !== null);
    const hasLoginIndicators = loginIndicators.some(el => el !== null);
    return hasAppIndicators && !hasLoginIndicators;
  }

  getPageContext() {
    const url = new URL(window.location.href);
    const projectMatch = url.pathname.match(/\/projects?\/(\d+)/i);
    const projectId = projectMatch ? projectMatch[1] : null;
    let pageType = 'unknown';
    if (url.pathname.includes('/project')) pageType = 'project';
    else if (url.pathname.includes('/document')) pageType = 'document';
    else if (url.pathname.includes('/contact')) pageType = 'contact';
    else if (url.pathname.includes('/search')) pageType = 'search';
    else if (url.pathname === '/' || url.pathname.includes('/dashboard')) pageType = 'dashboard';
    return {
      url: url.href,
      pathname: url.pathname,
      pageType,
      projectId,
      isLoggedIn: this.isLoggedIn()
    };
  }

  disconnect() {
    this.observers.forEach(observer => observer.disconnect());
    this.observers = [];
    clearTimeout(this.urlChangeTimeout);
    clearTimeout(this.domChangeTimeout);
  }
}

// TokenExtractor class (from src/content/extractor.js)
class TokenExtractor {
  constructor() {
    this.listeners = new Set();
    this.interceptedTokens = null;
  }

  setupInterception() {
    console.log('[Filevine Plugin] Setting up network interception...');
    
    try {
      // Use script.src instead of textContent to bypass CSP
      const script = document.createElement('script');
      script.id = 'filevine-plugin-interception-script';
      
      // Remove existing script if present
      const existing = document.getElementById('filevine-plugin-interception-script');
      if (existing) {
        existing.remove();
      }
      
      // Load interception script from extension file (bypasses CSP)
      script.src = chrome.runtime.getURL('src/content/interception.js');
      script.onload = () => {
        console.log('[Filevine Plugin] Interception script loaded successfully');
        script.remove(); // Clean up after load
      };
      script.onerror = () => {
        console.error('[Filevine Plugin] Failed to load interception script file');
      };
      
      // OLD INLINE APPROACH (blocked by CSP) - keeping for reference
      /*
      script.textContent = `
        (function() {
          // Immediate log to verify script runs
          console.log('[Filevine Plugin] Network interception script executing (page context)');
          
          try {
            // Intercept fetch - check both requests and responses
            const originalFetch = window.fetch;
            window.fetch = async function(...args) {
              const url = typeof args[0] === 'string' ? args[0] : args[0]?.url || args[0]?.href;
              const options = typeof args[0] === 'object' && !args[0]?.url ? args[0] : args[1] || {};
              
              // Check for Authorization header in outgoing requests
              if (options.headers) {
                const authHeader = options.headers.get ? options.headers.get('Authorization') : 
                                 options.headers.Authorization || options.headers.authorization;
                if (authHeader && authHeader.startsWith('Bearer ')) {
                  const token = authHeader.substring(7);
                  console.log('[Filevine Plugin] Found Authorization header in fetch request!');
                  window.postMessage({
                    type: 'FV_PLUGIN_TOKEN',
                    token: token,
                    expiresIn: 3600 // Default, will be updated if we get actual expiry
                  }, '*');
                }
              }
              
              const response = await originalFetch.apply(this, args);
              const responseUrl = response.url || url;
              
              // Check response for token endpoints
              if (responseUrl && (
                responseUrl.includes('connect/token') || 
                responseUrl.includes('identity.filevine.com') ||
                responseUrl.includes('/token')
              )) {
                console.log('[Filevine Plugin] Token endpoint detected:', responseUrl);
                try {
                  const clone = response.clone();
                  const data = await clone.json();
                  if (data.access_token) {
                    console.log('[Filevine Plugin] Token found in response!');
                    window.postMessage({
                      type: 'FV_PLUGIN_TOKEN',
                      token: data.access_token,
                      expiresIn: data.expires_in
                    }, '*');
                  }
                } catch (e) {
                  // Try text if JSON fails
                  try {
                    const text = await response.clone().text();
                    const data = JSON.parse(text);
                    if (data.access_token) {
                      console.log('[Filevine Plugin] Token found in text response!');
                      window.postMessage({
                        type: 'FV_PLUGIN_TOKEN',
                        token: data.access_token,
                        expiresIn: data.expires_in
                      }, '*');
                    }
                  } catch (e2) {
                    console.log('[Filevine Plugin] Could not parse token response:', e2);
                  }
                }
              }
              return response;
            };
            
            // Intercept XMLHttpRequest
            const originalOpen = XMLHttpRequest.prototype.open;
            const originalSend = XMLHttpRequest.prototype.send;
            const originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
            
            // Store headers to check later
            XMLHttpRequest.prototype._fvHeaders = {};
            
            XMLHttpRequest.prototype.open = function(method, url, ...rest) {
              this._fvUrl = url;
              this._fvHeaders = {};
              return originalOpen.call(this, method, url, ...rest);
            };
            
            // Intercept setRequestHeader to capture Authorization headers
            XMLHttpRequest.prototype.setRequestHeader = function(name, value) {
              if (name.toLowerCase() === 'authorization' && value.startsWith('Bearer ')) {
                const token = value.substring(7);
                console.log('[Filevine Plugin] Found Authorization header via setRequestHeader!', this._fvUrl);
                window.postMessage({
                  type: 'FV_PLUGIN_TOKEN',
                  token: token,
                  expiresIn: 3600
                }, '*');
              }
              this._fvHeaders[name.toLowerCase()] = value;
              return originalSetRequestHeader.call(this, name, value);
            };
            
            XMLHttpRequest.prototype.send = function(...args) {
              // Check stored headers for Authorization
              const authHeader = this._fvHeaders['authorization'];
              if (authHeader && authHeader.startsWith('Bearer ')) {
                const token = authHeader.substring(7);
                console.log('[Filevine Plugin] Found Authorization header in XHR send!', this._fvUrl);
                window.postMessage({
                  type: 'FV_PLUGIN_TOKEN',
                  token: token,
                  expiresIn: 3600
                }, '*');
              }
              
              // Check for token endpoint responses
              if (this._fvUrl && (
                this._fvUrl.includes('connect/token') || 
                this._fvUrl.includes('identity.filevine.com') ||
                this._fvUrl.includes('/token')
              )) {
                console.log('[Filevine Plugin] XHR token endpoint detected:', this._fvUrl);
                this.addEventListener('load', function() {
                  try {
                    const data = JSON.parse(this.responseText);
                    if (data.access_token) {
                      console.log('[Filevine Plugin] Token found in XHR response!');
                      window.postMessage({
                        type: 'FV_PLUGIN_TOKEN',
                        token: data.access_token,
                        expiresIn: data.expires_in
                      }, '*');
                    }
                  } catch (e) {
                    console.log('[Filevine Plugin] XHR token parse error:', e);
                  }
                });
              }
              return originalSend.apply(this, args);
            };
            
            console.log('[Filevine Plugin] Network interception active (page context)');
          } catch (error) {
            console.error('[Filevine Plugin] Failed to setup interception:', error);
          }
        })();
      `;
      */
      
      // Inject script file (bypasses CSP)
      if (document.head) {
        document.head.appendChild(script);
        console.log('[Filevine Plugin] Interception script file injected');
      } else if (document.documentElement) {
        document.documentElement.appendChild(script);
        console.log('[Filevine Plugin] Interception script file injected');
      } else {
        // Wait for DOM
        const injectWhenReady = () => {
          if (document.head || document.documentElement) {
            (document.head || document.documentElement).appendChild(script);
            console.log('[Filevine Plugin] Interception script file injected (delayed)');
          } else {
            setTimeout(injectWhenReady, 100);
          }
        };
        injectWhenReady();
      }
    } catch (error) {
      console.error('[Filevine Plugin] Failed to inject interception script:', error);
    }

    window.addEventListener('message', (event) => {
      if (event.source !== window) return;
      if (event.data?.type !== 'FV_PLUGIN_TOKEN') return;
      console.log('[Filevine Plugin] Intercepted token from network');
      this.handleInterceptedToken(event.data.token, event.data.expiresIn);
    });
  }

  handleInterceptedToken(accessToken, expiresIn) {
    const orgId = this.extractOrgIdFromPage();
    this.interceptedTokens = {
      accessToken,
      orgId,
      expiresIn
    };
    this.listeners.forEach(callback => {
      try {
        callback(this.interceptedTokens);
      } catch (e) {
        console.error('[Filevine Plugin] Token listener error:', e);
      }
    });
  }

  async extractTokens() {
    if (this.interceptedTokens) {
      return this.interceptedTokens;
    }
    const storageTokens = this.extractFromStorage();
    if (storageTokens?.accessToken) {
      return storageTokens;
    }
    const pageTokens = this.extractFromPageState();
    if (pageTokens?.accessToken) {
      return pageTokens;
    }
    const oidcTokens = this.extractFromOidc();
    if (oidcTokens?.accessToken) {
      return oidcTokens;
    }
    return null;
  }

  extractFromStorage() {
    const possibleKeys = ['fv_access_token', 'access_token', 'fv_auth', 'auth', 'oidc.user', 'token'];
    for (const storage of [localStorage, sessionStorage]) {
      for (const key of possibleKeys) {
        try {
          const value = storage.getItem(key);
          if (!value) continue;
          try {
            const parsed = JSON.parse(value);
            if (parsed.access_token) {
              return {
                accessToken: parsed.access_token,
                orgId: parsed.org_id || parsed.orgId || this.extractOrgIdFromPage()
              };
            }
            if (parsed.accessToken) {
              return {
                accessToken: parsed.accessToken,
                orgId: parsed.orgId || this.extractOrgIdFromPage()
              };
            }
          } catch {
            if (this.isJwtFormat(value)) {
              return {
                accessToken: value,
                orgId: this.extractOrgIdFromPage()
              };
            }
          }
        } catch (e) {}
      }
      try {
        for (let i = 0; i < storage.length; i++) {
          const key = storage.key(i);
          if (key?.includes('oidc.user') || key?.includes('filevine')) {
            const value = storage.getItem(key);
            if (value) {
              try {
                const parsed = JSON.parse(value);
                if (parsed.access_token) {
                  return {
                    accessToken: parsed.access_token,
                    orgId: this.extractOrgIdFromPage()
                  };
                }
              } catch {}
            }
          }
        }
      } catch {}
    }
    
    // Also check cookies (though HTTP-only cookies won't be accessible)
    try {
      const cookies = document.cookie.split(';');
      for (const cookie of cookies) {
        const [name, value] = cookie.trim().split('=');
        if (!value) continue;
        
        // Check for JWT in cookies
        if (this.isJwtFormat(value)) {
          console.log('[Filevine Plugin] Found JWT in cookie:', name);
          return {
            accessToken: decodeURIComponent(value),
            orgId: this.extractOrgIdFromPage()
          };
        }
        
        // Check for token-related cookie names
        if (name.toLowerCase().includes('token') || 
            name.toLowerCase().includes('auth') ||
            name.toLowerCase().includes('jwt')) {
          console.log('[Filevine Plugin] Found auth-related cookie:', name);
          // Try to parse as JWT
          const decoded = decodeURIComponent(value);
          if (this.isJwtFormat(decoded)) {
            return {
              accessToken: decoded,
              orgId: this.extractOrgIdFromPage()
            };
          }
        }
      }
    } catch (e) {
      console.log('[Filevine Plugin] Cookie check error:', e);
    }
    
    return null;
  }

  extractFromPageState() {
    const statePatterns = ['__FILEVINE_STATE__', '__APP_STATE__', '__REDUX_STATE__', 'filevineConfig', 'appConfig'];
    for (const pattern of statePatterns) {
      try {
        const state = window[pattern];
        if (state?.auth?.accessToken) {
          return {
            accessToken: state.auth.accessToken,
            orgId: state.auth.orgId || state.org?.id || this.extractOrgIdFromPage()
          };
        }
      } catch {}
    }
    return null;
  }

  extractFromOidc() {
    try {
      for (let i = 0; i < localStorage.length; i++) {
        const key = localStorage.key(i);
        if (key?.startsWith('oidc.user:')) {
          const value = localStorage.getItem(key);
          const parsed = JSON.parse(value);
          if (parsed?.access_token) {
            return {
              accessToken: parsed.access_token,
              orgId: this.extractOrgIdFromPage()
            };
          }
        }
      }
    } catch {}
    return null;
  }

  extractOrgIdFromPage() {
    const urlParams = new URLSearchParams(window.location.search);
    if (urlParams.has('orgId')) {
      return urlParams.get('orgId');
    }
    const pathMatch = window.location.pathname.match(/\/org\/(\d+)/);
    if (pathMatch) {
      return pathMatch[1];
    }
    return null;
  }

  isJwtFormat(str) {
    if (!str || typeof str !== 'string') return false;
    const parts = str.split('.');
    return parts.length === 3 && parts.every(p => p.length > 0);
  }

  onTokenReceived(callback) {
    this.listeners.add(callback);
    return () => this.listeners.delete(callback);
  }
}

// Main initialization code
// UIInjector class (from src/content/ui-injector.js)
class UIInjector {
  constructor() {
    this.injectedButtons = new Map();
    this.observer = null;
    this.isInjecting = false;
    this.recipientModal = null;
    this.lastClickedFile = null; // Track last clicked file for Echomark toolbar button
  }

  async start() {
    console.log('[UI Injector] Starting UI injection');

    // Load RecipientModal dynamically
    try {
      const baseUrl = chrome.runtime.getURL('');
      const modalModule = await import(`${baseUrl}src/content/recipient-modal.js`);
      this.recipientModal = new modalModule.RecipientModal();
      console.log('[UI Injector] RecipientModal loaded');
    } catch (error) {
      console.error('[UI Injector] Failed to load RecipientModal:', error);
    }

    this.injectUI();

    this.observer = new MutationObserver(() => {
      if (!this.isInjecting) {
        this.isInjecting = true;
        setTimeout(() => {
          this.injectUI();
          this.isInjecting = false;
        }, 500);
      }
    });

    this.observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }

  stop() {
    if (this.observer) {
      this.observer.disconnect();
      this.observer = null;
    }
  }

  injectUI() {
    this.injectIntoDocumentRows();
    this.injectToolbarButton();
    this.injectEchomarkToolbarButton();
  }

  injectIntoDocumentRows() {
    console.log('[UI Injector] Starting document row scan...');

    // Look for document rows - Filevine uses specific structure
    // Based on inspection, documents have data-doc-id attribute on li elements
    const selectors = [
      'li[data-doc-id]',              // Most specific - list items with doc ID
      'li[id^="list-item-file-"]',    // List items with file prefix
      '[data-testid*="doc-item"]',    // Test ID based items
      '.doc-list-item',               // Fallback selectors
      '.document-item',
      '.file-item'
    ];

    let totalFound = 0;
    let dicomFound = 0;

    selectors.forEach(selector => {
      const items = document.querySelectorAll(selector);
      if (items.length > 0) {
        console.log(`[UI Injector] Found ${items.length} elements matching "${selector}"`);
      }

      items.forEach(item => {
        totalFound++;
        const filename = this.getFilenameFromElement(item);
        console.log(`[UI Injector] Element ${totalFound}: filename="${filename}"`);

        const docId = this.getDocIdFromElement(item);
        if (!docId) {
          console.warn('[UI Injector] No document ID found for', filename);
          return;
        }

        // Check if this is a DICOM file
        if (this.isDicomFile(filename)) {
          dicomFound++;
          console.log(`[UI Injector] DICOM file found: ${filename}`);

          const key = `dicom-${selector}-${docId}`;
          if (!this.injectedButtons.has(key)) {
            const button = this.createViewButton(docId, filename);

            // Wrap button in a container with margin for proper spacing
            const buttonWrapper = document.createElement('span');
            buttonWrapper.className = 'fv-plugin-button-wrapper';
            buttonWrapper.style.cssText = 'display: inline-block; margin-right: 10px;';
            buttonWrapper.appendChild(button);

            // Try to insert button before the file size/author info
            const metadataSection = item.querySelector('[class*="metadata"], [class*="file-info"], [class*="doc-info"]');

            if (metadataSection) {
              metadataSection.parentNode.insertBefore(buttonWrapper, metadataSection);
              this.injectedButtons.set(key, true);
              console.log('[UI Injector] ✓ Injected DICOM button before metadata for', filename);
            } else {
              const actionsArea = this.findOrCreateActionsArea(item);
              if (actionsArea) {
                actionsArea.insertBefore(buttonWrapper, actionsArea.firstChild);
                this.injectedButtons.set(key, true);
                console.log('[UI Injector] ✓ Injected DICOM button in actions area for', filename);
              }
            }
          }
        }

        // Echomark watermarking is now handled via toolbar button only
        // No inline buttons in document rows

        // Add click listener to detect selection changes for Echomark toolbar button
        // This is inside the items.forEach loop, so 'item' is the actual DOM element
        if (!item.dataset.echomarkListenerAdded) {
          item.addEventListener('click', () => {
            // Store the clicked file info
            if (this.supportsEchomark(filename)) {
              this.lastClickedFile = { docId, filename, element: item };
              console.log('[UI Injector] Echomark-compatible file clicked:', filename);
            } else {
              this.lastClickedFile = null;
              console.log('[UI Injector] Non-Echomark file clicked, clearing selection');
            }

            // Delay slightly to allow Filevine's selection logic to run first
            setTimeout(() => {
              this.injectEchomarkToolbarButton();
            }, 100);
          });
          item.dataset.echomarkListenerAdded = 'true';
        }
      });
    });

    console.log(`[UI Injector] Scan complete: ${totalFound} elements checked, ${dicomFound} DICOM files found`);
  }

  injectToolbarButton() {
    // Find all DICOM files in the current view
    const dicomFiles = this.findAllDicomFilesInView();

    if (dicomFiles.length === 0) {
      // Remove toolbar button if it exists and no DICOM files found
      const existingButton = document.querySelector('.fv-plugin-toolbar-dicom-btn');
      if (existingButton) {
        existingButton.remove();
        console.log('[UI Injector] Removed toolbar button (no DICOM files in view)');
      }
      return;
    }

    // Check if button already exists
    if (document.querySelector('.fv-plugin-toolbar-dicom-btn')) {
      console.log('[UI Injector] Toolbar button already exists');
      return;
    }

    console.log(`[UI Injector] Found ${dicomFiles.length} DICOM files in view, adding toolbar button`);

    // Find the document actions bar (the one with the action buttons)
    const actionsBar = document.querySelector('.svelte-doc-action-bar, [class*="doc-action"], [data-testid*="doc-action"]');

    if (!actionsBar) {
      console.warn('[UI Injector] Could not find document action bar');
      // Fallback to toolbar
      const toolbar = document.querySelector('.doc-section-toolbar, .section-toolbar');
      if (toolbar) {
        const button = this.createToolbarButton(dicomFiles.length);
        toolbar.insertBefore(button, toolbar.firstChild);
        console.log('[UI Injector] Toolbar button injected (fallback)');
      }
      return;
    }

    // Create toolbar button
    const button = this.createToolbarButton(dicomFiles.length);

    // Insert button at the end of the action bar (after other action buttons)
    actionsBar.appendChild(button);

    console.log('[UI Injector] Toolbar button injected into action bar');
  }

  injectEchomarkToolbarButton() {
    // Find the currently selected file (if any)
    const selectedFile = this.getSelectedEchomarkFile();

    // Remove existing button if present
    const existingButton = document.querySelector('.fv-plugin-toolbar-echomark-btn');
    if (existingButton) {
      existingButton.remove();
    }

    // Only show button if a single PDF/image file is selected
    if (!selectedFile) {
      return;
    }

    console.log('[UI Injector] Selected Echomark-compatible file:', selectedFile.filename);

    // Find the document actions bar
    const actionsBar = document.querySelector('.svelte-doc-action-bar, [class*="doc-action"], [data-testid*="doc-action"]');

    if (!actionsBar) {
      console.warn('[UI Injector] Could not find document action bar for Echomark button');
      return;
    }

    // Create Echomark toolbar button
    const button = this.createEchomarkToolbarButton(selectedFile);

    // Insert button at the end of the action bar
    actionsBar.appendChild(button);

    console.log('[UI Injector] Echomark toolbar button injected');
  }

  getSelectedEchomarkFile() {
    // First, try to use the tracked last clicked file
    if (this.lastClickedFile) {
      // Verify the element still exists in the DOM
      if (document.body.contains(this.lastClickedFile.element)) {
        console.log('[UI Injector] Using tracked file:', this.lastClickedFile.filename);
        return this.lastClickedFile;
      } else {
        console.log('[UI Injector] Tracked file element no longer in DOM');
        this.lastClickedFile = null;
      }
    }

    // Fallback: Look for selected/highlighted document row
    // Filevine typically adds 'selected', 'active', or 'highlighted' class to selected rows
    const selectors = [
      'li[data-doc-id].selected',
      'li[data-doc-id].active',
      'li[data-doc-id].highlighted',
      'li[data-doc-id][class*="selected"]',
      'li[data-doc-id][class*="active"]'
    ];

    for (const selector of selectors) {
      const items = document.querySelectorAll(selector);
      console.log(`[UI Injector] Checking selector "${selector}": found ${items.length} items`);

      // Only handle single selection for now
      if (items.length === 1) {
        const item = items[0];
        const docId = item.dataset.docId;
        const filename = this.getFilenameFromElement(item);

        if (docId && filename && this.supportsEchomark(filename)) {
          console.log('[UI Injector] Found selected Echomark file via CSS selector:', filename);
          return { docId, filename, element: item };
        }
      }
    }

    console.log('[UI Injector] No selected Echomark file found');
    return null;
  }

  createEchomarkToolbarButton(fileInfo) {
    const button = document.createElement('button');
    button.className = 'fv-plugin-toolbar-echomark-btn';
    button.innerHTML = `
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 18px; height: 18px; flex-shrink: 0; color: #800020;">
        <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
        <path d="M9 12l2 2 4-4"></path>
      </svg>
      <span style="font-size: 13px; font-weight: 500; color: #4a5568;">Echomark</span>
    `;
    button.title = `Protect ${fileInfo.filename} with Echomark forensic watermark`;
    button.dataset.docId = fileInfo.docId;
    button.dataset.filename = fileInfo.filename;

    // Styling to match Filevine action bar buttons
    button.style.cssText = `
      background: transparent;
      color: #4a5568;
      border: 1px solid #800020;
      border-radius: 4px;
      padding: 5px 12px;
      cursor: pointer;
      transition: all 0.15s ease;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      margin-left: 8px;
      height: 32px;
      white-space: nowrap;
      font-family: inherit;
    `;

    // Hover effect
    button.addEventListener('mouseenter', () => {
      button.style.background = '#f7fafc';
      button.style.borderColor = '#600018';
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
      button.style.borderColor = '#800020';
    });

    // Click handler
    button.addEventListener('click', async (e) => {
      e.preventDefault();
      e.stopPropagation();
      await this.handleProtectClick(fileInfo.docId, fileInfo.filename, button);
    });

    return button;
  }

  findAllDicomFilesInView() {
    const dicomFiles = [];
    const items = document.querySelectorAll('li[data-doc-id]');

    items.forEach(item => {
      const filename = this.getFilenameFromElement(item);
      if (this.isDicomFile(filename)) {
        const docId = this.getDocIdFromElement(item);
        if (docId) {
          dicomFiles.push({
            docId: docId,
            filename: filename
          });
        }
      }
    });

    return dicomFiles;
  }

  createToolbarButton(count) {
    const button = document.createElement('button');
    button.className = 'fv-plugin-toolbar-dicom-btn';
    button.innerHTML = `
      <svg width="18" height="18" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink: 0; color: #800020;">
        <path d="M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 19H5V5H19V19Z" fill="currentColor"/>
        <path d="M7 12H9V17H7V12ZM11 7H13V17H11V7ZM15 10H17V17H15V10Z" fill="currentColor"/>
      </svg>
      <span style="font-size: 13px; font-weight: 500; color: #4a5568;">View DICOM Series (${count})</span>
    `;
    button.title = `Open all ${count} DICOM files in viewer`;

    // Styling to match Filevine action bar buttons - transparent with maroon border
    button.style.cssText = `
      background: transparent;
      color: #4a5568;
      border: 1px solid #800020;
      border-radius: 4px;
      padding: 5px 12px;
      cursor: pointer;
      transition: all 0.15s ease;
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 6px;
      margin-left: 8px;
      height: 32px;
      white-space: nowrap;
      font-family: inherit;
    `;

    // Hover effect - subtle background on hover
    button.addEventListener('mouseenter', () => {
      button.style.background = '#f7fafc';
      button.style.borderColor = '#600018';
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
      button.style.borderColor = '#800020';
    });

    // Click handler
    button.addEventListener('click', async (e) => {
      e.preventDefault();
      e.stopPropagation();
      await this.handleToolbarButtonClick(button);
    });

    return button;
  }

  async handleToolbarButtonClick(button) {
    console.log('[UI Injector] Toolbar button clicked - opening DICOM series');

    const originalHTML = button.innerHTML;
    button.innerHTML = '⏳ Loading series...';
    button.disabled = true;
    button.style.opacity = '0.7';
    button.style.cursor = 'wait';

    try {
      const projectId = this.getProjectIdFromUrl();
      if (!projectId) {
        throw new Error('Could not determine project ID from URL');
      }

      // Get current folder information
      const folderInfo = this.getCurrentFolderInfo();
      console.log('[UI Injector] Current folder:', folderInfo);

      // Get all DICOM files
      const dicomFiles = this.findAllDicomFilesInView();
      console.log(`[UI Injector] Opening ${dicomFiles.length} DICOM files as series`);

      // Send message to background to open as series
      // Use a non-null folderId - if we can't determine it, use -1 to indicate "current view"
      const folderId = folderInfo.folderId !== null ? folderInfo.folderId : -1;

      const response = await chrome.runtime.sendMessage({
        type: 'open_folder_as_dicom',
        projectId: parseInt(projectId),
        folderId: folderId,
        folderName: folderInfo.folderName || 'Current View',
        docIds: dicomFiles.map(f => parseInt(f.docId)),
        filenames: dicomFiles.map(f => f.filename)  // Pass filenames too
      });

      if (response?.error) {
        throw new Error(response.error);
      }

      // Success
      button.innerHTML = '✓ Opened Series';
      button.style.background = 'linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%)';

      setTimeout(() => {
        button.innerHTML = originalHTML;
        button.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
        button.disabled = false;
        button.style.opacity = '1';
        button.style.cursor = 'pointer';
      }, 2000);

    } catch (error) {
      console.error('[UI Injector] Failed to open DICOM series:', error);

      button.innerHTML = '✗ Failed';
      button.style.background = 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)';

      this.showNotification(`Failed to open series: ${error.message}`, 'error');

      setTimeout(() => {
        button.innerHTML = originalHTML;
        button.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
        button.disabled = false;
        button.style.opacity = '1';
        button.style.cursor = 'pointer';
      }, 3000);
    }
  }

  getFilenameFromElement(element) {
    // Try data attribute first
    if (element.dataset.filename) return element.dataset.filename;

    // Try to find filename in common element classes
    const selectors = [
      '[class*="filename"]',
      '[class*="file-name"]',
      '[class*="document-name"]',
      '[class*="doc-name"]',
      '[class*="name"]',
      'span[title]',  // Often filename is in a span with title attribute
      'a[title]',     // Or in a link with title
    ];

    for (const selector of selectors) {
      const el = element.querySelector(selector);
      if (el) {
        // Check title attribute first (often contains full filename)
        const title = el.getAttribute('title');
        if (title && title.includes('.')) {
          return title.trim();
        }

        // Check text content
        const text = el.textContent.trim();
        if (text && text.includes('.')) {
          return text;
        }
      }
    }

    // Last resort: search all text content for filename pattern
    const text = element.textContent;
    // Match common file extensions (more comprehensive than just DICOM)
    const match = text.match(/[\w\s\-\(\)]+\.(pdf|dcm|dicom|jpg|jpeg|png|tiff|tif|bmp|gif|doc|docx|xls|xlsx|txt|zip|eml|xml)/i);
    return match ? match[0].trim() : '';
  }

  getDocIdFromElement(element) {
    // Try direct data-doc-id attribute (most common for Filevine)
    let docId = element.getAttribute('data-doc-id');
    if (docId) {
      console.log('[UI Injector] Found doc ID from data-doc-id:', docId);
      return docId;
    }

    // Try other data attributes
    docId = element.dataset.docId ||
           element.dataset.documentId ||
           element.dataset.id ||
           element.getAttribute('data-document-id');

    if (docId) {
      console.log('[UI Injector] Found doc ID from dataset:', docId);
      return docId;
    }

    // Try to extract from ID attribute (e.g., "list-item-file-123456")
    const id = element.getAttribute('id');
    if (id) {
      const match = id.match(/file-(\d+)/);
      if (match) {
        docId = match[1];
        console.log('[UI Injector] Found doc ID from id attribute:', id, '→', docId);
        return docId;
      }
    }

    // Try to find doc ID in child elements or links
    const links = element.querySelectorAll('a[href]');
    for (const link of links) {
      const href = link.getAttribute('href');
      // Match patterns like /doc/123 or /document/123 or ?docId=123
      const match = href.match(/\/(doc|document)\/(\d+)|[?&]docId=(\d+)|[?&]documentId=(\d+)/i);
      if (match) {
        docId = match[2] || match[3] || match[4];
        console.log('[UI Injector] Found doc ID in href:', href, '→', docId);
        return docId;
      }
    }

    console.warn('[UI Injector] Could not find doc ID in element:', element);
    return null;
  }

  isDicomFile(filename) {
    if (!filename) return false;
    const lower = filename.toLowerCase();
    return lower.endsWith('.dcm') || lower.endsWith('.dicom');
  }

  supportsEchomark(filename) {
    if (!filename) return false;
    const lower = filename.toLowerCase();

    // PDF support
    if (lower.endsWith('.pdf')) return true;

    // Image support
    const imageExts = ['.jpg', '.jpeg', '.png', '.tiff', '.tif', '.bmp', '.gif'];
    return imageExts.some(ext => lower.endsWith(ext));
  }

  getEchomarkMimeType(filename) {
    const lower = filename.toLowerCase();

    if (lower.endsWith('.pdf')) return 'application/pdf';
    if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
    if (lower.endsWith('.png')) return 'image/png';
    if (lower.endsWith('.tiff') || lower.endsWith('.tif')) return 'image/tiff';
    if (lower.endsWith('.bmp')) return 'image/bmp';
    if (lower.endsWith('.gif')) return 'image/gif';

    return 'application/octet-stream';
  }

  findOrCreateActionsArea(item) {
    // Try to find existing actions area
    let actionsArea = item.querySelector('[class*="action"], [class*="button"]');
    if (actionsArea) return actionsArea;

    // Create new actions area
    actionsArea = document.createElement('div');
    actionsArea.style.cssText = 'display: inline-flex; gap: 8px; align-items: center; margin-left: 12px;';
    item.appendChild(actionsArea);
    return actionsArea;
  }

  createViewButton(docId, filename) {
    const button = document.createElement('button');
    button.className = 'fv-plugin-view-dicom-btn';
    button.innerHTML = `
      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" style="flex-shrink: 0; color: #800020;">
        <path d="M19 3H5C3.9 3 3 3.9 3 5V19C3 20.1 3.9 21 5 21H19C20.1 21 21 20.1 21 19V5C21 3.9 20.1 3 19 3ZM19 19H5V5H19V19Z" fill="currentColor"/>
        <path d="M7 12H9V17H7V12ZM11 7H13V17H11V7ZM15 10H17V17H15V10Z" fill="currentColor"/>
      </svg>
      <span style="font-size: 12px; font-weight: 500; color: #4a5568;">View</span>
    `;
    button.title = `Open ${filename} in DICOM viewer`;
    button.dataset.docId = docId;
    button.dataset.filename = filename;

    button.style.cssText = `
      background: transparent;
      color: #4a5568;
      border: 1px solid #800020;
      border-radius: 3px;
      padding: 4px 10px;
      cursor: pointer;
      transition: all 0.15s ease;
      display: inline-flex;
      align-items: center;
      gap: 4px;
      white-space: nowrap;
      font-family: inherit;
      height: 28px;
    `;

    button.addEventListener('mouseenter', () => {
      button.style.background = '#f7fafc';
      button.style.borderColor = '#600018';
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
      button.style.borderColor = '#800020';
    });

    button.addEventListener('click', async (e) => {
      e.preventDefault();
      e.stopPropagation();
      await this.handleViewClick(docId, filename, button);
    });

    return button;
  }

  createProtectButton(docId, filename) {
    const button = document.createElement('button');
    button.className = 'fv-plugin-protect-btn';
    button.innerHTML = `
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width: 14px; height: 14px; flex-shrink: 0; color: #800020;">
        <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
        <path d="M9 12l2 2 4-4"></path>
      </svg>
      <span style="font-size: 12px; font-weight: 500; color: #4a5568;">Echomark</span>
    `;
    button.title = `Protect ${filename} with Echomark forensic watermark`;
    button.dataset.docId = docId;
    button.dataset.filename = filename;

    button.style.cssText = `
      background: transparent;
      color: #4a5568;
      border: 1px solid #800020;
      border-radius: 3px;
      padding: 4px 10px;
      cursor: pointer;
      transition: all 0.15s ease;
      display: inline-flex;
      align-items: center;
      gap: 4px;
      white-space: nowrap;
      font-family: inherit;
      height: 28px;
    `;

    button.addEventListener('mouseenter', () => {
      button.style.background = '#f7fafc';
      button.style.borderColor = '#600018';
    });

    button.addEventListener('mouseleave', () => {
      button.style.background = 'transparent';
      button.style.borderColor = '#800020';
    });

    button.addEventListener('click', async (e) => {
      e.preventDefault();
      e.stopPropagation();
      await this.handleProtectClick(docId, filename, button);
    });

    return button;
  }

  async handleProtectClick(docId, filename, button) {
    console.log('[UI Injector] Protect Document clicked:', { docId, filename });

    if (!this.recipientModal) {
      this.showNotification('Recipient modal not loaded', 'error');
      return;
    }

    try {
      // Get current project ID from URL
      const projectId = this.getProjectIdFromUrl();

      if (!projectId) {
        throw new Error('Could not determine project ID from URL');
      }

      // Show recipient selection modal
      await this.recipientModal.show(
        {
          docId,
          filename,
          projectId
        },
        async (recipientData, watermarkOptions) => {
          // Handle watermarking when user confirms
          await this.handleWatermarkDocument(docId, filename, projectId, recipientData, watermarkOptions);
        }
      );

    } catch (error) {
      console.error('[UI Injector] Failed to show protect modal:', error);
      this.showNotification(`Failed to protect document: ${error.message}`, 'error');
    }
  }

  /**
   * Handle document watermarking after recipient selection
   */
  async handleWatermarkDocument(docId, filename, projectId, recipientData, watermarkOptions) {
    console.log('[UI Injector] Starting watermarking:', {
      docId,
      filename,
      recipient: recipientData.recipientName
    });

    try {
      // Send message to background script to handle watermarking
      const response = await chrome.runtime.sendMessage({
        type: 'WATERMARK_DOCUMENT',
        docId: parseInt(docId),
        projectId: parseInt(projectId),
        filename: filename,
        mimeType: this.getEchomarkMimeType(filename),
        recipientData: recipientData,
        watermarkOptions: watermarkOptions
      });

      if (response?.error) {
        throw new Error(response.error);
      }

      // Success
      this.showNotification(
        `Document protected for ${recipientData.recipientName}`,
        'success'
      );

      console.log('[UI Injector] Watermarking complete');

    } catch (error) {
      console.error('[UI Injector] Watermarking failed:', error);
      throw error; // Re-throw so modal can handle it
    }
  }

  async handleViewClick(docId, filename, button) {
    console.log('[UI Injector] View DICOM clicked:', { docId, filename });

    const originalText = button.textContent;
    button.textContent = '⏳ Loading...';
    button.disabled = true;
    button.style.opacity = '0.7';
    button.style.cursor = 'wait';

    try {
      const projectId = this.getProjectIdFromUrl();
      if (!projectId) {
        throw new Error('Could not determine project ID from URL');
      }

      const response = await chrome.runtime.sendMessage({
        type: 'view_file_direct',  // Use lowercase to match constants.js
        docId: parseInt(docId),
        projectId: parseInt(projectId),
        filename: filename,
        mimeType: 'application/dicom'
      });

      if (response?.error) {
        throw new Error(response.error);
      }

      button.textContent = '✓ Opened';
      button.style.background = 'linear-gradient(135deg, #06b6d4 0%, #3b82f6 100%)';

      setTimeout(() => {
        button.textContent = originalText;
        button.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
        button.disabled = false;
        button.style.opacity = '1';
        button.style.cursor = 'pointer';
      }, 2000);

    } catch (error) {
      console.error('[UI Injector] Failed to open DICOM viewer:', error);

      button.textContent = '✗ Failed';
      button.style.background = 'linear-gradient(135deg, #ef4444 0%, #dc2626 100%)';

      this.showNotification(`Failed to open viewer: ${error.message}`, 'error');

      setTimeout(() => {
        button.textContent = originalText;
        button.style.background = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
        button.disabled = false;
        button.style.opacity = '1';
        button.style.cursor = 'pointer';
      }, 3000);
    }
  }

  getProjectIdFromUrl() {
    const url = window.location.href;
    const pathname = window.location.pathname;
    console.log('[UI Injector] Extracting project ID from URL:', url);
    console.log('[UI Injector] Pathname:', pathname);

    // Try various URL patterns
    // Pattern 1: /project/123/...
    let match = pathname.match(/\/project\/(\d+)/i);
    if (match) {
      console.log('[UI Injector] Found project ID in /project/ path:', match[1]);
      return match[1];
    }

    // Pattern 2: /projects/123/...
    match = pathname.match(/\/projects\/(\d+)/i);
    if (match) {
      console.log('[UI Injector] Found project ID in /projects/ path:', match[1]);
      return match[1];
    }

    // Pattern 3: Query parameters
    const params = new URLSearchParams(window.location.search);
    const projectId = params.get('projectId') || params.get('project_id');
    if (projectId) {
      console.log('[UI Injector] Found project ID in query params:', projectId);
      return projectId;
    }

    // Pattern 4: Hash-based routing (e.g., #/project/123)
    const hash = window.location.hash;
    if (hash) {
      match = hash.match(/\/project[s]?\/(\d+)/i);
      if (match) {
        console.log('[UI Injector] Found project ID in hash:', match[1]);
        return match[1];
      }
    }

    console.warn('[UI Injector] Could not extract project ID from URL:', url);
    return null;
  }

  getCurrentFolderInfo() {
    // Try to get folder info from the current view
    // Look for folder ID in URL or breadcrumbs

    // Method 1: Check URL for folder parameter
    const params = new URLSearchParams(window.location.search);
    let folderId = params.get('folderId') || params.get('folder_id');
    let folderName = params.get('folderName') || params.get('folder_name');

    // Method 2: Check for selected folder in breadcrumbs or header
    if (!folderName) {
      // Look for folder name in breadcrumb or header
      const breadcrumb = document.querySelector('[class*="breadcrumb"], .current-folder, [class*="folder-name"]');
      if (breadcrumb) {
        folderName = breadcrumb.textContent.trim();
      }
    }

    // Method 3: Try to find folder ID from DOM elements
    if (!folderId) {
      const folderElement = document.querySelector('[data-folder-id], [data-folderid]');
      if (folderElement) {
        folderId = folderElement.dataset.folderId || folderElement.dataset.folderid;
      }
    }

    // If still no folder info, check if we're at root
    if (!folderId) {
      // Look for folders in the list - if we see folders, we're at root level
      const hasFolders = document.querySelector('.folder-container, [class*="folder-"]');
      if (hasFolders) {
        folderName = 'All Documents';
        folderId = 0; // Root folder
      }
    }

    return {
      folderId: folderId ? parseInt(folderId) : null,
      folderName: folderName || 'Current View'
    };
  }

  showNotification(message, type = 'info') {
    const existing = document.querySelector('.fv-plugin-notification');
    if (existing) existing.remove();

    const notification = document.createElement('div');
    notification.className = 'fv-plugin-notification';
    notification.textContent = message;

    const colors = {
      info: { bg: '#3b82f6', border: '#2563eb' },
      success: { bg: '#10b981', border: '#059669' },
      error: { bg: '#ef4444', border: '#dc2626' },
      warning: { bg: '#f59e0b', border: '#d97706' }
    };

    const color = colors[type] || colors.info;

    notification.style.cssText = `
      position: fixed;
      top: 20px;
      right: 20px;
      background: ${color.bg};
      color: white;
      padding: 12px 20px;
      border-radius: 8px;
      border-left: 4px solid ${color.border};
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      z-index: 999999;
      font-size: 14px;
      font-weight: 500;
      max-width: 400px;
      animation: slideIn 0.3s ease;
    `;

    const style = document.createElement('style');
    style.textContent = `
      @keyframes slideIn {
        from { transform: translateX(400px); opacity: 0; }
        to { transform: translateX(0); opacity: 1; }
      }
    `;
    document.head.appendChild(style);

    document.body.appendChild(notification);

    setTimeout(() => notification.remove(), 5000);
  }
}

const detector = new TokenDetector();
const extractor = new TokenExtractor();
const uiInjector = new UIInjector();

async function initialize() {
  console.log('[Filevine Plugin] Initializing session detection...');

  if (!isFilevineUrl(window.location.href)) {
    console.log('[Filevine Plugin] Not a Filevine page, skipping');
    return;
  }

  extractor.setupInterception();

  const tokens = await extractor.extractTokens();
  
  if (tokens?.accessToken) {
    console.log('[Filevine Plugin] Found existing auth tokens');
    await sendTokensToBackground(tokens);
  } else {
    console.log('[Filevine Plugin] No existing tokens, waiting for auth...');
  }

  extractor.onTokenReceived((tokens) => {
    console.log('[Filevine Plugin] New tokens received');
    sendTokensToBackground(tokens);
  });

  detector.watchForChanges(() => {
    console.log('[Filevine Plugin] Page change detected, rechecking tokens');
    extractor.extractTokens().then(tokens => {
      if (tokens?.accessToken) {
        sendTokensToBackground(tokens);
      }
    });
  });

  // Start UI injection (adds buttons to Filevine UI)
  await uiInjector.start();
}

function isFilevineUrl(url) {
  try {
    const parsed = new URL(url);
    return parsed.hostname.includes('filevineapp.com');
  } catch {
    return false;
  }
}

async function sendTokensToBackground(tokens) {
  try {
    const response = await chrome.runtime.sendMessage({
      type: MESSAGE_TYPES.AUTH_DETECTED,
      ...tokens
    });
    
    if (response?.authenticated) {
      console.log('[Filevine Plugin] Background confirmed auth');
      showConnectionStatus('connected');
    } else if (response?.error) {
      console.error('[Filevine Plugin] Auth error:', response.error);
      showConnectionStatus('error', response.error);
    }
  } catch (error) {
    console.error('[Filevine Plugin] Failed to send tokens:', error);
  }
}

function showConnectionStatus(status, message = '') {
  console.log(`[Filevine Plugin] Connection status: ${status}`, message);
}

chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
  if (message.type === MESSAGE_TYPES.GET_AUTH_STATE) {
    extractor.extractTokens().then(tokens => {
      sendResponse({ hasTokens: !!tokens?.accessToken });
    });
    return true;
  }
});

if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', initialize);
} else {
  initialize();
}
