<head>
  <meta content="text/html; charset=utf-8" http-equiv="content-type" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{html_report_name}} - TestReport</title>
  <style>
    body {
      background-color: #f2f2f2;
      color: #333;
      margin: 0 auto;
      width: 960px;
    }
    #summary {
      width: 960px;
      margin-bottom: 20px;
    }
    #summary th {
      background-color: skyblue;
      padding: 5px 12px;
    }
    #summary td {
      background-color: lightblue;
      text-align: center;
      padding: 4px 8px;
    }
    .details {
      width: 960px;
      margin-bottom: 20px;
    }
    .details th {
      background-color: skyblue;
      padding: 5px 12px;
    }
    .details tr .passed {
      background-color: lightgreen;
    }
    .details tr .failed {
      background-color: red;
    }
    .details tr .unchecked {
      background-color: gray;
    }
    .details td {
      background-color: lightblue;
      padding: 5px 12px;
    }
    .details .detail {
      background-color: lightgrey;
      font-size: smaller;
      padding: 5px 10px;
      line-height: 20px;
      text-align: left;
    }
    .details .success {
      background-color: greenyellow;
    }
    .details .error {
      background-color: red;
    }
    .details .failure {
      background-color: salmon;
    }
    .details .skipped {
      background-color: gray;
    }

    .button {
      font-size: 1em;
      padding: 6px;
      width: 4em;
      text-align: center;
      background-color: #06d85f;
      border-radius: 20px/50px;
      cursor: pointer;
      transition: all 0.3s ease-out;
    }
    a.button{
      color: gray;
      text-decoration: none;
      display: inline-block;
    }
    .button:hover {
      background: #2cffbd;
    }

    .overlay {
      position: fixed;
      top: 0;
      bottom: 0;
      left: 0;
      right: 0;
      background: rgba(0, 0, 0, 0.7);
      transition: opacity 500ms;
      visibility: hidden;
      opacity: 0;
      line-height: 25px;
    }
    .overlay:target {
      visibility: visible;
      opacity: 1;
    }

    .popup {
      margin: 70px auto;
      padding: 20px;
      background: #fff;
      border-radius: 10px;
      width: 50%;
      position: relative;
      transition: all 3s ease-in-out;
    }

    .popup h2 {
      margin-top: 0;
      color: #333;
      font-family: Tahoma, Arial, sans-serif;
    }
    .popup .close {
      position: absolute;
      top: 20px;
      right: 30px;
      transition: all 200ms;
      font-size: 30px;
      font-weight: bold;
      text-decoration: none;
      color: #333;
    }
    .popup .close:hover {
      color: #06d85f;
    }
    .popup .content {
      max-height: 80%;
      overflow: auto;
      text-align: left;
    }
    .popup .separator {
      color:royalblue
    }

    @media screen and (max-width: 700px) {
      .box {
        width: 70%;
      }
      .popup {
        width: 70%;
      }
    }

    /* 折叠内容样式 */
    .collapsible-content {
      position: relative;
    }
    
    .content-preview {
      max-height: 200px;
      overflow: hidden;
      position: relative;
    }
    
    .content-preview.collapsed::after {
      content: "";
      position: absolute;
      bottom: 0;
      left: 0;
      right: 0;
      height: 40px;
      background: linear-gradient(transparent, rgba(255,255,255,0.9));
      pointer-events: none;
    }
    
    .expand-btn, .collapse-btn, .copy-btn {
      background: #06d85f;
      color: white;
      border: none;
      padding: 5px 10px;
      border-radius: 3px;
      cursor: pointer;
      margin-top: 5px;
      margin-left: 5px;
      font-size: 12px;
    }
    
    .expand-btn:hover, .collapse-btn:hover, .copy-btn:hover {
      background: #2cffbd;
    }
    
    .content-full {
      max-height: none;
    }

    /* JSON树形展示样式 */
    .json-tree {
      font-family: monospace;
      line-height: 1.5;
    }
    
    .json-key {
      color: #881391;
      font-weight: bold;
    }
    
    .json-string {
      color: #C41E3A;
    }
    
    .json-number {
      color: #1C00CF;
    }
    
    .json-boolean {
      color: #0D7377;
    }
    
    .json-null {
      color: #808080;
    }
    
    .json-toggle {
      cursor: pointer;
      user-select: none;
      color: #666;
      font-weight: bold;
      margin-right: 5px;
    }
    
    .json-toggle:hover {
      background: #f0f0f0;
    }
    
    .json-collapsed {
      display: none;
    }
    
    .json-item {
      margin-left: 20px;
    }
    
    .json-bracket {
      color: #666;
      font-weight: bold;
    }
    
    .json-lazy-placeholder {
      color: #999;
      font-style: italic;
      cursor: pointer;
      padding: 2px 4px;
      background: #f5f5f5;
      border-radius: 3px;
      display: inline-block;
      margin: 2px;
    }
    
    .json-lazy-placeholder:hover {
      background: #e0e0e0;
      color: #666;
    }

  </style>
</head>

<body>
  <h1>Test Report: {{html_report_name}}</h1>

  <h2>Summary</h2>
  <table id="summary">
    <tr>
      <th>START AT</th>
      <td colspan="4">{{time.start_datetime}}</td>
    </tr>
    <tr>
      <th>DURATION</th>
      <td colspan="4">{{ '%0.3f'| format(time.duration|float) }} seconds</td>
    </tr>
    <tr>
      <th>PLATFORM</th>
      <td>HttpRunner {{ platform.httprunner_version }} </td>
      <td>{{ platform.python_version }} </td>
      <td colspan="2">{{ platform.platform }}</td>
    </tr>
    <tr>
      <th>STAT</th>
      <th colspan="2">TESTCASES (success/fail)</th>
      <th colspan="2">TESTSTEPS (success/fail/error/skip)</th>
    </tr>
    <tr>
      <td>total (details) =></td>
      <td colspan="2">{{stat.testcases.total}} ({{stat.testcases.success}}/{{stat.testcases.fail}})</td>
      <td colspan="2">{{stat.teststeps.total}} ({{stat.teststeps.successes}}/{{stat.teststeps.failures}}/{{stat.teststeps.errors}}/{{stat.teststeps.skipped}})</td>
    </tr>
  </table>

  <h2>Details</h2>

  {% for test_suite_summary in details %}
  {% set suite_index = loop.index %}
  <h3>{{test_suite_summary.name}}</h3>
  <table id="suite_{{suite_index}}" class="details">
    <tr>
      <td>TOTAL: {{test_suite_summary.stat.total}}</td>
      <td>SUCCESS: {{test_suite_summary.stat.successes}}</td>
      <td>FAILED: {{test_suite_summary.stat.failures}}</td>
      <td>ERROR: {{test_suite_summary.stat.errors}}</td>
      <td>SKIPPED: {{test_suite_summary.stat.skipped}}</td>
    </tr>
    <tr>
      <th>Status</th>
      <th colspan="2">Name</th>
      <th>Response Time</th>
      <th>Detail</th>
    </tr>

    {% for record in test_suite_summary.records %}
    {% set record_index = "{}_{}".format(suite_index, loop.index) %}
    {% set record_meta_datas = record.meta_datas_expanded %}
    <tr id="record_{{record_index}}">
      <th class="{{record.status}}" style="width:5em;">{{record.status}}</th>
      <td colspan="2">{{record.name}}</td>
      <td style="text-align:center;width:6em;">{{ record.response_time }} ms</td>
      <td class="detail">

        {% for meta_data in record_meta_datas %}
        {% set meta_data_index = "{}_{}".format(record_index, loop.index) %}
        <a class="button" href="#popup_log_{{meta_data_index}}">log-{{loop.index}}</a>
        <div id="popup_log_{{meta_data_index}}" class="overlay">
          <div class="popup">
            <h2>Request and Response data</h2>
            <a class="close" href="#record_{{meta_data_index}}">&times;</a>

            <div class="content">
              <h3>Name: {{ meta_data.name }}</h3>

              {% for req_resp in meta_data.data %}

              {% if loop.index > 1 %}
              <div class="separator">==================================== redirect to ====================================</div>
              {% endif %}

              <h3>Request:</h3>
              <div style="overflow: auto">
                <table>
                  {% for key, value in req_resp.request.items() %}
                    <tr>
                      <th>{{key}}</th>
                      <td>
                        {% if key in ["headers", "body"] %}
                            <pre>{{ value | e }}</pre>
                        {% else %}
                            {{value}}
                        {% endif %}
                      </td>
                    </tr>
                  {% endfor %}
                </table>
              </div>

              <h3>Response:</h3>
              <div style="overflow: auto">
                <table>
                    {% for key, value in req_resp.response.items() %}
                      <tr>
                        <th>{{key}}</th>
                        <td>
                          {% if key  == "headers" %}
                            <pre>{{ value | e }}</pre>
                          {% elif key == "body" %}
                            {% if "image" in req_resp.response.content_type %}
                              <img src="{{ req_resp.response.content }}" />
                            {% else %}
                              <pre>{{ value | e }}</pre>
                            {% endif %}
                          {% else %}
                            {{ value }}
                          {% endif %}
                        </td>
                      </tr>
                    {% endfor %}
                  </table>
              </div>
              {% endfor %}

              <h3>Validators:</h3>
                <div style="overflow: auto">
                {% set validate_extractors = meta_data.validators.validate_extractor %}
                {% if validate_extractors %}
                <table>
                    <tr>
                      <th>check</th>
                      <th>comparator</th>
                      <th>expect value</th>
                      <th>actual value</th>
                    </tr>
                    {% for validator in validate_extractors %}
                    <tr>
                      {% if validator.check_result == "pass" %}
                      <td class="passed">
                      {% elif validator.check_result == "fail" %}
                      <td class="failed">
                      {% elif validator.check_result == "unchecked" %}
                      <td class="unchecked">
                      {% endif %}
                        <pre>{{validator.check | e}}</pre>
                      </td>
                      <td>{{validator.comparator}}</td>
                      <td><pre>{{validator.expect | e}}</pre></td>
                      <td><pre>{{validator.check_value | e}}</pre></td>
                    </tr>
                    {% endfor %}
                </table>
                {% endif %}

                {% set validate_script = meta_data.validators.validate_script %}
                {% if validate_script %}
                    <h3>Scripts:</h3>
                    {% if validate_script.details %}
                    <!-- 新版逐条显示的validate_script结果 -->
                    <table>
                        <tr>
                            <th>script</th>
                            <th>output</th>
                        </tr>
                        {% for script_item in validate_script.details %}
                        <tr>
                            {% if script_item.check_result == "pass" %}
                            <td class="passed"><pre>{{script_item.script | e}}</pre></td>
                            {% elif script_item.check_result == "fail" %}
                            <td class="failed"><pre>{{script_item.script | e}}</pre></td>
                            {% endif %}
                            <td><pre>{{script_item.output | e}}</pre></td>
                        </tr>
                        {% endfor %}
                    </table>
                    {% else %}
                    <!-- 旧版整体显示的validate_script结果（向后兼容） -->
                    <table>
                        <tr>
                            <th>validate script</th><th>output</th>
                        </tr>
                        <tr>
                            <td><pre>{{validate_script.validate_script | safe}}</pre></td>
                            {% if validate_script.check_result == "pass" %}
                            <td class="passed">
                            {% elif validate_script.check_result == "fail" %}
                            <td class="failed">
                            {% endif %}
                            {{validate_script.output}}
                            </td>
                        </tr>
                    </table>
                    {% endif %}
                {% endif %}
              </div>

              <h3>Statistics:</h3>
              <div style="overflow: auto">
                <table>
                  <tr>
                      <th>content_size(bytes)</th>
                      <td>{{ meta_data.stat.content_size }}</td>
                    </tr>
                  <tr>
                    <th>response_time(ms)</th>
                    <td>{{ meta_data.stat.response_time_ms }}</td>
                  </tr>
                  <tr>
                    <th>elapsed(ms)</th>
                    <td>{{ meta_data.stat.elapsed_ms }}</td>
                  </tr>
                </table>
              </div>

            </div>
          </div>
        </div>
        {% endfor %}

        {% if record.attachment %}
          <a class="button" href="#popup_attachment_{{record_index}}">traceback</a>
          <div id="popup_attachment_{{record_index}}" class="overlay">
            <div class="popup">
              <h2>Traceback Message</h2>
              <a class="close" href="#record_{{record_index}}">&times;</a>
              <div class="content"><pre>{{ record.attachment | e }}</pre></div>
            </div>
          </div>
        {% endif %}

      </td>
    </tr>
  {% endfor %}
  </table>
  {% endfor %}

<script>
// 判断是否为JSON字符串或类JSON字符串
function isJsonString(str) {
  try {
    var obj = JSON.parse(str);
    return typeof obj === 'object' && obj !== null;
  } catch (e) {
    // 检查是否是Python字典格式的字符串
    var trimmed = str.trim();
    if ((trimmed.startsWith('{') && trimmed.endsWith('}')) || 
        (trimmed.startsWith('[') && trimmed.endsWith(']'))) {
      // 尝试将Python格式转换为JSON格式
      try {
        var converted = trimmed
          .replace(/'/g, '"')           // 单引号转双引号
          .replace(/None/g, 'null')     // Python None转null
          .replace(/True/g, 'true')     // Python True转true
          .replace(/False/g, 'false');  // Python False转false
        JSON.parse(converted);
        return true;
      } catch (e2) {
        return false;
      }
    }
    return false;
  }
}

// 宽松解析：同时支持严格 JSON 与 Python 字典风格（单引号、None/True/False）
function tryParseJsonOrPythonLike(str) {
  if (!str) return null;
  var trimmed = str.trim();

  // 1) 严格 JSON 直接解析
  try {
    return JSON.parse(trimmed);
  } catch (e1) {}

  // 2) Python -> JSON 简单替换再解析（保留双引号不动，避免破坏已合法内容）
  try {
    var convertedForJson = trimmed
      .replace(/\bNone\b/g, 'null')
      .replace(/\bTrue\b/g, 'true')
      .replace(/\bFalse\b/g, 'false')
      // 先将键名的单引号替换为双引号：'key': → "key":
      .replace(/'([^'\\]*(?:\\.[^'\\]*)*)'\s*:/g, function(_, key){
        return '"' + key.replace(/\\"/g, '"') + '"' + ':';
      })
      // 再尽量将作为值的纯单引号字符串替成双引号（避免破坏已经是双引号的值）：
      .replace(/:\s*'([^'\\]*(?:\\.[^'\\]*)*)'(?=\s*[\]\},])/g, function(_, val){
        return ': "' + val.replace(/\\"/g, '"') + '"';
      })
      // 数组中的值（以逗号或[开始）
      .replace(/([\[,]\s*)'([^'\\]*(?:\\.[^'\\]*)*)'(?=\s*[\]\},])/g, function(_, prefix, val){
        return prefix + '"' + val.replace(/\\"/g, '"') + '"';
      });

    return JSON.parse(convertedForJson);
  } catch (e2) {}

  // 3) 最后兜底：在安全字符检查通过后，按 JS 字面量求值（仅当形如 { ... } 或 [ ... ]）
  try {
    var startsOk = (trimmed.startsWith('{') && trimmed.endsWith('}')) || (trimmed.startsWith('[') && trimmed.endsWith(']'));
    if (!startsOk) return null;

    // 基础安全检查：禁止明显可执行语句或危险符号
    var forbidden = /[;=<>`$]|\bfunction\b|\bconstructor\b|\bwhile\b|\bfor\b|\bclass\b|\bimport\b|=>/;
    if (forbidden.test(trimmed)) return null;

    var jsLiteral = trimmed
      .replace(/\bNone\b/g, 'null')
      .replace(/\bTrue\b/g, 'true')
      .replace(/\bFalse\b/g, 'false');

    // 使用 Function 返回对象字面量
    return (new Function('return (' + jsLiteral + ')'))();
  } catch (e3) {}

  return null;
}

// 创建JSON树形结构 - 优化版本
function createJsonTreeLazy(obj, depth = 0) {
  if (depth > 10) return '<span class="json-string">...</span>'; // 防止递归过深
  
  if (obj === null) {
    return '<span class="json-null">null</span>';
  }
  
  if (typeof obj === 'string') {
    return '<span class="json-string">"' + escapeHtml(obj) + '"</span>';
  }
  
  if (typeof obj === 'number') {
    return '<span class="json-number">' + obj + '</span>';
  }
  
  if (typeof obj === 'boolean') {
    return '<span class="json-boolean">' + obj + '</span>';
  }
  
  if (Array.isArray(obj)) {
    if (obj.length === 0) {
      return '<span class="json-bracket">[]</span>';
    }
    
    var id = 'json_' + Math.random().toString(36).substr(2, 9);
    var html = '<span class="json-bracket">[</span>';
    
    // 前2层默认展开，深层级需要点击展开
    var isExpanded = depth < 2;
    var toggleIcon = isExpanded ? '▼' : '▶';
    var itemClass = isExpanded ? 'json-item' : 'json-item json-collapsed';
    
    html += '<span class="json-toggle" onclick="toggleJsonNodeLazy(\'' + id + '\', this)" title="点击展开/折叠">' + toggleIcon + '</span>';
    html += '<div id="' + id + '" class="' + itemClass + '" data-obj="' + escapeHtml(JSON.stringify(obj)) + '" data-depth="' + depth + '">';
    
    // 前2层直接显示内容，深层级使用占位符
    if (depth < 2) {
      for (var i = 0; i < obj.length; i++) {
        html += '<div>' + createJsonTreeLazy(obj[i], depth + 1);
        if (i < obj.length - 1) html += ',';
        html += '</div>';
      }
    } else {
      html += '<div class="json-lazy-placeholder">点击展开查看 ' + obj.length + ' 项...</div>';
    }
    
    html += '</div><span class="json-bracket">]</span>';
    return html;
  }
  
  if (typeof obj === 'object') {
    var keys = Object.keys(obj);
    if (keys.length === 0) {
      return '<span class="json-bracket">{}</span>';
    }
    
    var id = 'json_' + Math.random().toString(36).substr(2, 9);
    var html = '<span class="json-bracket">{</span>';
    
    // 前2层默认展开，深层级需要点击展开
    var isExpanded = depth < 2;
    var toggleIcon = isExpanded ? '▼' : '▶';
    var itemClass = isExpanded ? 'json-item' : 'json-item json-collapsed';
    
    html += '<span class="json-toggle" onclick="toggleJsonNodeLazy(\'' + id + '\', this)" title="点击展开/折叠">' + toggleIcon + '</span>';
    html += '<div id="' + id + '" class="' + itemClass + '" data-obj="' + escapeHtml(JSON.stringify(obj)) + '" data-depth="' + depth + '">';
    
    // 前2层直接显示内容，深层级使用占位符
    if (depth < 2) {
      for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        html += '<div><span class="json-key">"' + escapeHtml(key) + '"</span>: ' + createJsonTreeLazy(obj[key], depth + 1);
        if (i < keys.length - 1) html += ',';
        html += '</div>';
      }
    } else {
      html += '<div class="json-lazy-placeholder">点击展开查看 ' + keys.length + ' 个属性...</div>';
    }
    
    html += '</div><span class="json-bracket">}</span>';
    return html;
  }
  
  return '<span class="json-string">' + escapeHtml(String(obj)) + '</span>';
}


// 切换JSON节点的展开/折叠状态 - 懒加载版本
function toggleJsonNodeLazy(id, toggleElement) {
  var element = document.getElementById(id);
  
  if (element.classList.contains('json-collapsed')) {
    // 展开节点
    element.classList.remove('json-collapsed');
    toggleElement.innerHTML = '▼';
    toggleElement.title = '点击折叠';
    
    // 检查是否需要懒加载内容
    var placeholder = element.querySelector('.json-lazy-placeholder');
    if (placeholder) {
      // 需要懒加载，替换占位符为实际内容
      loadJsonNodeContent(element, placeholder);
    }
  } else {
    // 折叠节点
    element.classList.add('json-collapsed');
    toggleElement.innerHTML = '▶';
    toggleElement.title = '点击展开';
  }
}

// 递归展开容器内所有JSON节点
function expandAllJsonNodes(container) {
  var collapsedNodes = container.querySelectorAll('.json-item.json-collapsed');
  collapsedNodes.forEach(function(node) {
    var toggle = node.parentNode.querySelector('.json-toggle');
    if (toggle && node.classList.contains('json-collapsed')) {
      // 展开节点
      node.classList.remove('json-collapsed');
      toggle.innerHTML = '▼';
      toggle.title = '点击折叠';
      
      // 检查是否需要懒加载内容
      var placeholder = node.querySelector('.json-lazy-placeholder');
      if (placeholder) {
        // 需要懒加载，替换占位符为实际内容
        loadJsonNodeContent(node, placeholder);
        // 递归处理新加载的内容
        setTimeout(function() {
          expandAllJsonNodes(node);
        }, 10);
      } else {
        // 递归处理子节点
        expandAllJsonNodes(node);
      }
    }
  });
}

// 递归折叠容器内所有JSON节点  
function collapseAllJsonNodes(container) {
  var expandedNodes = container.querySelectorAll('.json-item:not(.json-collapsed)');
  expandedNodes.forEach(function(node) {
    var toggle = node.parentNode.querySelector('.json-toggle');
    if (toggle && !node.classList.contains('json-collapsed')) {
      // 折叠节点
      node.classList.add('json-collapsed');
      toggle.innerHTML = '▶';
      toggle.title = '点击展开';
    }
  });
}

// 懒加载JSON节点内容
function loadJsonNodeContent(element, placeholder) {
  try {
    var objData = element.getAttribute('data-obj');
    var depth = parseInt(element.getAttribute('data-depth')) || 0;
    
    if (objData) {
      var obj = JSON.parse(unescapeHtml(objData));
      
      // 清空现有内容
      element.innerHTML = '';
      
      // 生成实际内容
      if (Array.isArray(obj)) {
        for (var i = 0; i < obj.length; i++) {
          var div = document.createElement('div');
          div.innerHTML = createJsonTreeLazy(obj[i], depth + 1);
          if (i < obj.length - 1) div.innerHTML += ',';
          element.appendChild(div);
        }
      } else if (typeof obj === 'object' && obj !== null) {
        var keys = Object.keys(obj);
        for (var i = 0; i < keys.length; i++) {
          var key = keys[i];
          var div = document.createElement('div');
          div.innerHTML = '<span class="json-key">"' + escapeHtml(key) + '"</span>: ' + createJsonTreeLazy(obj[key], depth + 1);
          if (i < keys.length - 1) div.innerHTML += ',';
          element.appendChild(div);
        }
      }
    }
  } catch (e) {
    console.error('Failed to load JSON node content:', e);
    placeholder.textContent = '加载失败';
  }
}


// HTML转义
function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}

// HTML实体解码 - 完整版
function unescapeHtml(text) {
  // 先处理数字编码的实体
  text = text.replace(/&#(\d+);/g, function(match, dec) {
    return String.fromCharCode(dec);
  });
  
  // 再处理十六进制编码的实体
  text = text.replace(/&#x([0-9a-fA-F]+);/g, function(match, hex) {
    return String.fromCharCode(parseInt(hex, 16));
  });
  
  // 最后处理命名实体
  var map = {
    '&amp;': '&',
    '&lt;': '<',
    '&gt;': '>',
    '&quot;': '"',
    '&apos;': "'",
    '&#039;': "'",
    '&#39;': "'",
    '&nbsp;': ' '
  };
  
  return text.replace(/&amp;|&lt;|&gt;|&quot;|&apos;|&#039;|&#39;|&nbsp;/g, function(m) { return map[m]; });
}

// 复制文本到剪贴板（带降级方案）
function copyTextToClipboard(text) {
  if (!text && text !== '') return;
  if (navigator.clipboard && navigator.clipboard.writeText) {
    navigator.clipboard.writeText(text).catch(function() {
      fallbackCopyText(text);
    });
  } else {
    fallbackCopyText(text);
  }
}

function fallbackCopyText(text) {
  try {
    var textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed';
    textarea.style.top = '-9999px';
    textarea.style.left = '-9999px';
    document.body.appendChild(textarea);
    textarea.focus();
    textarea.select();
    document.execCommand('copy');
  } finally {
    if (textarea && textarea.parentNode) {
      textarea.parentNode.removeChild(textarea);
    }
  }
}

// 处理内容的折叠展开 - 优化版本，处理指定弹窗内所有pre元素
function setupCollapsibleContentForPopup(popup) {
  // 选择弹窗内的所有pre元素（包括验证器表格中的）
  var preElements = popup.querySelectorAll('pre');
  var totalElements = preElements.length;
  var processedCount = 0;
  
  // 分片处理，避免阻塞UI
  function processBatch(startIndex) {
    var batchSize = Math.min(5, totalElements - startIndex); // 每批处理5个元素
    var endIndex = startIndex + batchSize;
    
    for (var i = startIndex; i < endIndex && i < totalElements; i++) {
      processPreElement(preElements[i]);
      processedCount++;
    }
    
    // 如果还有更多元素需要处理，继续下一批
    if (endIndex < totalElements) {
      requestAnimationFrame(function() {
        processBatch(endIndex);
      });
    }
  }
  
  // 开始分片处理
  if (totalElements > 0) {
    processBatch(0);
  }
}

// 处理单个pre元素
function processPreElement(pre) {
  var content = pre.textContent || pre.innerText;
  // 先进行HTML实体解码，处理HTML模板中的转义字符
  var decodedContent = unescapeHtml(content);
  var processedElement = pre;
  var finalContent = decodedContent;
  
  // 跳过过短的内容，避免处理简单文本
  if (decodedContent.trim().length < 10) {
    return;
  }
  
  
  // 步骤1：如果是JSON/Python字典，转换为树形结构
  try {
    var parsedObj = tryParseJsonOrPythonLike(decodedContent.trim());
    if (parsedObj !== null) {
      var jsonHtml = createJsonTreeLazy(parsedObj, 0); // 使用懒加载版本
      var jsonDiv = document.createElement('div');
      jsonDiv.className = 'json-tree';
      jsonDiv.innerHTML = jsonHtml;
      pre.parentNode.replaceChild(jsonDiv, pre);
      processedElement = jsonDiv;
      // 使用格式化后的JSON字符串计算行数
      var formattedJson = JSON.stringify(parsedObj, null, 2);
      finalContent = formattedJson;
    }
  } catch (e) {
    // 解析失败则保持原样
  }
  
  // 步骤2：判断是否需要折叠（转换后超过15行）
  var lineCount = finalContent.split('\n').length;
  
  if (lineCount > 15) {
    var wrapper = document.createElement('div');
    wrapper.className = 'collapsible-content';
    
    var preview = document.createElement('div');
    preview.className = 'content-preview collapsed';
    preview.appendChild(processedElement.cloneNode(true));
    
    var expandBtn = document.createElement('button');
    expandBtn.className = 'expand-btn';
    expandBtn.textContent = '展开 (' + lineCount + ' 行)';
    
    expandBtn.onclick = function() {
      preview.classList.remove('collapsed');
      preview.classList.add('content-full');
      expandBtn.style.display = 'none';
      collapseBtn.style.display = 'inline-block';
      
      // 同时展开所有JSON节点
      expandAllJsonNodes(preview);
    };
    
    var collapseBtn = document.createElement('button');
    collapseBtn.className = 'collapse-btn';
    collapseBtn.textContent = '折叠';
    collapseBtn.style.display = 'none';
    collapseBtn.onclick = function() {
      preview.classList.add('collapsed');
      preview.classList.remove('content-full');
      expandBtn.style.display = 'inline-block';
      collapseBtn.style.display = 'none';
      
      // 同时折叠所有JSON节点
      collapseAllJsonNodes(preview);
    };

    var copyBtn = document.createElement('button');
    copyBtn.className = 'copy-btn';
    copyBtn.textContent = '复制';
    copyBtn.onclick = function() {
      copyTextToClipboard(finalContent);
      var prev = copyBtn.textContent;
      copyBtn.textContent = '已复制';
      setTimeout(function(){ copyBtn.textContent = prev; }, 1200);
    };
    
    wrapper.appendChild(preview);
    wrapper.appendChild(expandBtn);
    wrapper.appendChild(collapseBtn);
    wrapper.appendChild(copyBtn);
    
    processedElement.parentNode.replaceChild(wrapper, processedElement);
  }
}


// 存储已处理的弹窗，避免重复处理
var processedPopups = new Set();

// 性能优化：延迟加载和按需处理
// 移除全局初始化，改为按需处理

// 按需处理单个弹窗的内容
function processPopupContent(popupId) {
  if (processedPopups.has(popupId)) {
    return; // 已经处理过的弹窗，跳过
  }
  
  var popup = document.getElementById(popupId);
  if (!popup) {
    return;
  }
  
  // 标记为已处理
  processedPopups.add(popupId);
  
  // 性能监控：开始计时
  var startTime = performance.now();
  
  // 添加加载指示器
  var loadingIndicator = createLoadingIndicator();
  var popupContent = popup.querySelector('.content');
  if (popupContent) {
    popupContent.appendChild(loadingIndicator);
  }
  
  // 使用 requestAnimationFrame 进行非阻塞处理
  requestAnimationFrame(function() {
    try {
      setupCollapsibleContentForPopup(popup);
    } finally {
      // 移除加载指示器
      if (loadingIndicator && loadingIndicator.parentNode) {
        loadingIndicator.parentNode.removeChild(loadingIndicator);
      }
      
      // 性能监控：记录处理时间
      var endTime = performance.now();
      var processingTime = endTime - startTime;
      performanceStats.processedPopups++;
      performanceStats.totalProcessingTime += processingTime;
      
      // 首次处理时显示性能统计
      if (performanceStats.processedPopups === 1) {
        setTimeout(showPerformanceStats, 100);
      }
    }
  });
}

// 创建加载指示器
function createLoadingIndicator() {
  var indicator = document.createElement('div');
  indicator.style.cssText = 'padding: 10px; text-align: center; color: #666; font-size: 14px;';
  indicator.innerHTML = '⏳ 正在处理内容，请稍候...';
  return indicator;
}

// 性能监控
var performanceStats = {
  processedPopups: 0,
  totalProcessingTime: 0,
  startTime: Date.now()
};

// 添加性能统计信息到页面
function showPerformanceStats() {
  var statsDiv = document.createElement('div');
  statsDiv.style.cssText = 'position: fixed; bottom: 10px; right: 10px; background: rgba(0,0,0,0.8); color: white; padding: 10px; border-radius: 5px; font-size: 12px; z-index: 9999;';
  statsDiv.innerHTML = '📊 性能统计<br>已处理弹窗: ' + performanceStats.processedPopups + '<br>总处理时间: ' + performanceStats.totalProcessingTime.toFixed(2) + 'ms<br>页面加载时间: ' + (Date.now() - performanceStats.startTime).toFixed(0) + 'ms';
  
  document.body.appendChild(statsDiv);
  
  // 3秒后隐藏统计信息
  setTimeout(function() {
    if (statsDiv && statsDiv.parentNode) {
      statsDiv.parentNode.removeChild(statsDiv);
    }
  }, 3000);
}

// 监听弹窗点击事件，延迟处理内容
document.addEventListener('DOMContentLoaded', function() {
  // 为所有log按钮添加点击事件监听
  var logButtons = document.querySelectorAll('a.button[href^="#popup_log_"]');
  logButtons.forEach(function(button) {
    button.addEventListener('click', function(e) {
      var popupId = this.getAttribute('href').substring(1); // 移除开头的#
      // 延迟一小段时间等待弹窗显示，然后处理内容
      setTimeout(function() {
        processPopupContent(popupId);
      }, 50);
    });
  });
});
</script>
</body>