User Tools

Site Tools


test1:subject6
(async () => {
  const rootHandle = await window.showDirectoryPicker();

  const origin = location.origin;
  const currentUrl = new URL(location.href);

  const textDecoder = new TextDecoder();

  const normalizeUrl = (u) => {
    try {
      const x = new URL(u, location.href);
      if (!/^https?:$/.test(x.protocol)) return null;
      if (x.origin !== origin) return null; // same-origin only
      x.hash = "";
      return x;
    } catch {
      return null;
    }
  };

  const toLocalPathParts = (urlObj, isHtml = false) => {
    let path = decodeURIComponent(urlObj.pathname);

    if (!path || path === "/") {
      return ["index.html"];
    }

    const parts = path.split("/").filter(Boolean);

    const last = parts[parts.length - 1] || "";
    const hasExt = /\.[a-zA-Z0-9]{1,10}$/.test(last);

    if (isHtml) {
      if (hasExt && /\.html?$/i.test(last)) {
        return parts;
      }
      if (hasExt) {
        return parts;
      }
      return [...parts, "index.html"];
    }

    if (!hasExt) {
      return [...parts, "index.html"];
    }

    return parts;
  };

  const getOrCreateDir = async (baseHandle, dirParts) => {
    let h = baseHandle;
    for (const part of dirParts) {
      h = await h.getDirectoryHandle(part, { create: true });
    }
    return h;
  };

  const writeFile = async (baseHandle, pathParts, blob) => {
    const dirs = pathParts.slice(0, -1);
    const fileName = pathParts[pathParts.length - 1];
    const dirHandle = await getOrCreateDir(baseHandle, dirs);
    const fileHandle = await dirHandle.getFileHandle(fileName, { create: true });
    const writable = await fileHandle.createWritable();
    await writable.write(blob);
    await writable.close();
  };

  const collected = new Map();

  const addUrl = (u, type = "asset") => {
    const nu = normalizeUrl(u);
    if (!nu) return;
    const key = nu.href;
    if (!collected.has(key)) {
      collected.set(key, { url: nu, type });
    }
  };

  // current page
  addUrl(location.href, "html");

  // DOM resources
  document.querySelectorAll("script[src]").forEach(el => addUrl(el.src, "asset"));
  document.querySelectorAll('link[href]').forEach(el => addUrl(el.href, "asset"));
  document.querySelectorAll("img[src]").forEach(el => addUrl(el.src, "asset"));
  document.querySelectorAll("[style]").forEach(el => {
    const style = el.getAttribute("style") || "";
    const re = /url\((['"]?)(.*?)\1\)/g;
    let m;
    while ((m = re.exec(style))) addUrl(m[2], "asset");
  });

  // CSS rules
  for (const sheet of Array.from(document.styleSheets)) {
    try {
      for (const rule of Array.from(sheet.cssRules || [])) {
        const txt = rule.cssText || "";
        const re = /url\((['"]?)(.*?)\1\)/g;
        let m;
        while ((m = re.exec(txt))) addUrl(m[2], "asset");
      }
    } catch (e) {
      // cross-origin stylesheet access can fail
    }
  }

  // Performance resources
  performance.getEntriesByType("resource").forEach(r => addUrl(r.name, "asset"));

  // Save HTML of current page into path-based index.html
  const currentHtmlParts = toLocalPathParts(currentUrl, true);
  const htmlBlob = new Blob([document.documentElement.outerHTML], {
    type: "text/html;charset=utf-8"
  });
  await writeFile(rootHandle, currentHtmlParts, htmlBlob);

  let ok = 0;
  let fail = 0;
  const failed = [];

  for (const { url, type } of collected.values()) {
    if (type === "html" && url.href === currentUrl.href) continue;

    try {
      const res = await fetch(url.href, { credentials: "include" });
      if (!res.ok) throw new Error(`HTTP ${res.status}`);

      const blob = await res.blob();
      const pathParts = toLocalPathParts(url, false);
      await writeFile(rootHandle, pathParts, blob);
      ok++;
      console.log("saved:", url.href, "->", pathParts.join("/"));
    } catch (e) {
      fail++;
      failed.push({ url: url.href, error: String(e) });
      console.warn("failed:", url.href, e);
    }
  }

  // save fail report
  const report = {
    page: location.href,
    savedHtmlAs: currentHtmlParts.join("/"),
    savedCount: ok,
    failedCount: fail,
    failed
  };

  await writeFile(
    rootHandle,
    ["_download_report.json"],
    new Blob([JSON.stringify(report, null, 2)], { type: "application/json" })
  );

  console.log("DONE");
  console.log("HTML:", currentHtmlParts.join("/"));
  console.log("saved assets:", ok);
  console.log("failed assets:", fail);
})();
test1/subject6.txt · Last modified: by admin