# JavaScript Extraction Guide


**Last Updated:** 2025-11-20

Step-by-step guide for extracting calculator functions from PHP files into external JavaScript files.

## Overview

This guide covers the process of extracting large calculator functions (>20KB) from PHP files into external JavaScript files for better caching, maintainability, and performance.

## When to Extract

### ✅ Extract When:

- Calculator function > 20KB
- Function has no PHP dependencies (no `<?php ?>` tags)
- Function is self-contained (all helpers included)
- Function can be wrapped in IIFE
- Function is used in only one page (or can be shared)

### ❌ Don't Extract When:

- Function uses PHP variables (`<?php echo $var; ?>`)
- Function depends on PHP-generated data
- Function is small (< 10KB)
- Function has complex PHP dependencies
- Function is tightly coupled with PHP logic

## Step-by-Step Extraction Process

### Step 1: Analyze the Function

Before extraction, analyze the function:

1. **Check for PHP dependencies:**

   ```bash
   grep -n "<?php\|<?=" v2/pages/tools_[name].php | grep -A 5 "function [calculator]Calculator"
   ```

2. **Identify helper functions:**

   ```bash
   grep -n "function " v2/pages/tools_[name].php | grep -v "Calculator"
   ```

3. **Check for global dependencies:**

   ```bash
   grep -n "window\." v2/pages/tools_[name].php | head -20
   ```

4. **Measure function size:**
   ```bash
   # Count lines in function
   sed -n '/function [calculator]Calculator/,/^    }/p' v2/pages/tools_[name].php | wc -l
   ```

### Step 2: Create External File

Create the external JavaScript file:

```bash
touch v2/js/tools-[name]-calculator.js
```

**File naming convention:**

- Pattern: `tools-[name]-calculator.js`
- Use kebab-case for names
- Examples:
  - `tools-arbeitszeit-calculator.js`
  - `tools-stundenlohn-calculator.js`
  - `tools-mehrwertsteuer-calculator.js`

### Step 3: Extract Function Code

1. **Copy function from PHP file:**

   - Locate function definition: `function [calculator]Calculator() {`
   - Copy entire function including closing brace
   - Include all helper functions used by the calculator

2. **Wrap in IIFE:**

   ```javascript
   /**
    * Calculator Name
    * Extracted from tools_[name].php
    *
    * @description Brief description of what calculator does
    * @requires Alpine.js
    * @requires [other dependencies if any]
    */

   (function() {
       'use strict';

       // Function definition here
       function [calculator]Calculator() {
           // ... function code ...
       }

       // Assign to window immediately
       window.[calculator]Calculator = [calculator]Calculator;

       // Register with Alpine.js
       if (typeof Alpine !== 'undefined') {
           Alpine.data('[calculator]Calculator', [calculator]Calculator);
       } else {
           document.addEventListener('alpine:init', function() {
               if (typeof Alpine !== 'undefined') {
                   Alpine.data('[calculator]Calculator', [calculator]Calculator);
               }
           });
       }
   })();
   ```

### Step 4: Handle Helper Functions

**Helper functions that should be extracted:**

- Formatting functions (`formatCurrency`, `formatNumber`, `formatDate`)
- Export functions (`exportPDF`, `exportCSV`, `generatePDFHTML`)
- Calculation helpers specific to the calculator

**Helper functions that remain in PHP:**

- Setup functions (e.g., `setupCalculatorWatchers`, `initialCalculation`)
- Config loading (e.g., `window.paypalConfig`)
- Utility functions shared across multiple calculators

**Pattern for helper functions:**

```javascript
(function() {
    'use strict';

    // Helper function outside main calculator
    function formatCurrency(amount, currency) {
        // Helper implementation
    }

    // Main calculator function
    function [calculator]Calculator() {
        return {
            // Can call formatCurrency here
            displayAmount() {
                return formatCurrency(this.amount, 'EUR');
            }
        };
    }

    // Registration code
    window.[calculator]Calculator = [calculator]Calculator;
    // ... Alpine.js registration ...
})();
```

### Step 5: Update PHP File

Replace the inline function with a script tag:

```php
<!-- Calculator Name -->
<script src="/v2/js/tools-[name]-calculator.js?v=<?php echo filemtime(__DIR__ . '/../js/tools-[name]-calculator.js'); ?>"></script>

<script>
    // OLD CODE - REMOVED: Calculator function extracted to external file
    // See: v2/js/tools-[name]-calculator.js
    /*
    function [calculator]Calculator() {
        // ... old code ...
    }

    // OLD CODE - REMOVED: Registration code moved to external file
    */
</script>
```

**Important:**

- Keep old code commented out for reference (can be removed later)
- Use `filemtime()` for cache busting
- Place script tag in `<head>` section

### Step 6: Update Minification Script

Add file to `minify-assets.js`:

```javascript
const jsFiles = [
  // ... existing files ...
  "v2/js/tools-[name]-calculator.js",
  // ... more files ...
];
```

### Step 7: Run Minification

```bash
npm run minify
```

This generates:

- `v2/js/tools-[name]-calculator.min.js` (minified version)

### Step 8: Verify Extraction

1. **Check syntax:**

   ```bash
   node --check v2/js/tools-[name]-calculator.js
   ```

2. **Test in browser:**

   - Open page: `http://localhost:8003/v2/pages/tools_[name].php`
   - Open DevTools Console (F12)
   - Check for function definition:
     ```javascript
     typeof window.[calculator]Calculator !== 'undefined'
     // Should return "function"
     ```

3. **Test calculator functionality:**

   - Enter test values
   - Verify calculations work
   - Test export functionality (if applicable)
   - Check for console errors

4. **Check Network tab:**
   - Verify `tools-[name]-calculator.js` loads (200 status)
   - Check file size is reasonable
   - Verify cache busting works (`?v=timestamp`)

## Common Issues and Solutions

### Issue 1: Syntax Errors After Extraction

**Symptoms:**

- Console shows JavaScript syntax errors
- File doesn't load

**Causes:**

- Helper functions incorrectly placed inside return object
- Missing semicolons or unclosed braces
- Stray backticks or parentheses

**Solution:**

1. **Check for helper functions inside return object:**

   ```javascript
   // ❌ BAD
   function calculator() {
     return {
       calculate() {
         /* ... */
       },
       formatCurrency() {
         /* ... */
       }, // Helper inside return object
     };
   }

   // ✅ GOOD
   function formatCurrency(amount) {
     // Helper outside return object
   }

   function calculator() {
     return {
       calculate() {
         /* ... */
       },
     };
   }
   ```

2. **Run syntax check:**

   ```bash
   node --check v2/js/tools-[name]-calculator.js
   ```

3. **Check for stray characters:**
   ```bash
   grep -n "^\s*)\s*$" v2/js/tools-[name]-calculator.js
   grep -n "^\s*}\s*$" v2/js/tools-[name]-calculator.js
   ```

### Issue 2: Function Not Defined

**Symptoms:**

- Console shows: `[calculator]Calculator is not defined`
- Calculator doesn't initialize

**Causes:**

- Script tag not in `<head>` section
- File path incorrect
- Browser caching old version

**Solution:**

1. **Verify script tag placement:**

   ```bash
   grep -n "tools-[name]-calculator.js" v2/pages/tools_[name].php
   ```

   Should be in `<head>` section (before `</head>` tag).

2. **Check file path:**

   ```bash
   ls -la v2/js/tools-[name]-calculator.js
   ```

3. **Clear browser cache:**
   - Hard refresh: Cmd+Shift+R (Mac) / Ctrl+Shift+R (Windows)
   - Or clear cache in DevTools

### Issue 3: Helper Functions Not Available

**Symptoms:**

- Calculator works but helper functions fail
- Errors about undefined functions

**Causes:**

- Helper functions not extracted
- Helper functions not in correct scope
- Helper functions depend on PHP variables

**Solution:**

1. **Check if helpers are in PHP file:**

   ```bash
   grep -n "function formatCurrency\|function exportPDF" v2/pages/tools_[name].php
   ```

2. **Move helpers to external file:**

   - Extract helper functions
   - Place outside main calculator function
   - Ensure they're accessible

3. **Check dependencies:**
   - Verify helpers don't use PHP variables
   - Check for `window.` references

### Issue 4: Alpine.js Registration Issues

**Symptoms:**

- Function defined but Alpine.js doesn't recognize it
- Console shows Alpine errors

**Causes:**

- Registration pattern incorrect
- Alpine.js not ready when function registers
- Missing immediate check

**Solution:**

Use robust registration pattern:

```javascript
// Assign to window immediately
window.[calculator]Calculator = [calculator]Calculator;

// Register with Alpine.js - check immediately first
if (typeof Alpine !== 'undefined') {
    Alpine.data('[calculator]Calculator', [calculator]Calculator);
} else {
    // Fallback to event listener if Alpine not ready yet
    document.addEventListener('alpine:init', function() {
        if (typeof Alpine !== 'undefined') {
            Alpine.data('[calculator]Calculator', [calculator]Calculator);
        }
    });
}
```

## Example: Complete Extraction

### Before (PHP File)

```php
<script>
    function arbeitszeitCalculator() {
        return {
            hours: 0,
            minutes: 0,
            calculate() {
                // Calculation logic
            },
            formatTime(hours, minutes) {
                // Formatting logic
            }
        };
    }

    window.arbeitszeitCalculator = arbeitszeitCalculator;
    // ... registration code ...
</script>
```

### After (External File)

**`v2/js/tools-arbeitszeit-calculator.js`:**

```javascript
/**
 * Arbeitszeit Calculator
 * Extracted from tools_arbeitszeitrechner.php
 *
 * @description Calculator for work time calculations
 * @requires Alpine.js
 */

(function () {
  "use strict";

  // Helper function outside main calculator
  function formatTime(hours, minutes) {
    // Formatting logic
  }

  // Main calculator function
  function arbeitszeitCalculator() {
    return {
      hours: 0,
      minutes: 0,
      calculate() {
        // Calculation logic
      },
    };
  }

  // Assign to window immediately
  window.arbeitszeitCalculator = arbeitszeitCalculator;

  // Register with Alpine.js
  if (typeof Alpine !== "undefined") {
    Alpine.data("arbeitszeitCalculator", arbeitszeitCalculator);
  } else {
    document.addEventListener("alpine:init", function () {
      if (typeof Alpine !== "undefined") {
        Alpine.data("arbeitszeitCalculator", arbeitszeitCalculator);
      }
    });
  }
})();
```

### After (PHP File)

```php
<!-- Arbeitszeit Calculator -->
<script src="/v2/js/tools-arbeitszeit-calculator.js?v=<?php echo filemtime(__DIR__ . '/../js/tools-arbeitszeit-calculator.js'); ?>"></script>

<script>
    // OLD CODE - REMOVED: Calculator function extracted to external file
    // See: v2/js/tools-arbeitszeit-calculator.js
</script>
```

## Checklist

Use this checklist for each extraction:

- [ ] Function analyzed for PHP dependencies
- [ ] Helper functions identified
- [ ] External file created with correct naming
- [ ] Function extracted and wrapped in IIFE
- [ ] Helper functions placed outside return object
- [ ] Alpine.js registration pattern implemented
- [ ] PHP file updated with script tag
- [ ] Old code commented out (for reference)
- [ ] Minification script updated
- [ ] Minification run successfully
- [ ] Syntax check passed (`node --check`)
- [ ] Function defined in browser (`typeof window.[calculator]Calculator`)
- [ ] Calculator functionality tested
- [ ] No console errors
- [ ] Network tab shows file loading correctly
- [ ] Cache busting works (`?v=timestamp`)

## Benefits of Extraction

1. **Better caching:** External JS files cached by browser
2. **Maintainability:** Easier to edit and debug
3. **Performance:** Smaller PHP files, parallel JS loading
4. **Code organization:** Separation of concerns
5. **Reusability:** Can be shared across pages if needed
6. **Minification:** Can be minified separately
7. **Version control:** Easier to track changes

## Reference

- **Cursor Rules:** `.cursor/rules/tools-pages.mdc` - JavaScript Extraction Pattern section
- **Testing Guide:** `docs/tools-pages-testing.md` - JavaScript Extraction Pattern section
- **Minification:** `minify-assets.js` - Add files to `jsFiles` array

## Extracted Calculators

The following calculators have been successfully extracted:

1. **arbeitszeitCalculator** → `v2/js/tools-arbeitszeit-calculator.js` (167KB)
2. **stundenlohnrechnerCalculator** → `v2/js/tools-stundenlohn-calculator.js` (91KB)
3. **mehrwertsteuerCalculator** → `v2/js/tools-mehrwertsteuer-calculator.js` (77KB)
4. **midijobCalculator** → `v2/js/tools-midijob-calculator.js` (67KB)
5. **zuschlagsrechnerCalculator** → `v2/js/tools-zuschlags-calculator.js` (29KB)
6. **paypalCalculator** → `v2/js/tools-paypal-calculator.js` (16KB)
