403Webshell
Server IP : 54.37.205.81  /  Your IP : 216.73.216.76
Web Server : nginx/1.22.1
System : Linux vps-249481fa 6.1.0-50-cloud-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.176-1 (2026-07-02) x86_64
User : debian ( 1000)
PHP Version : 8.2.32
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : ON  |  Pkexec : OFF
Directory :  /proc/thread-self/root/tmp/copyrightbridge-v0.4.0/assets/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /proc/thread-self/root/tmp/copyrightbridge-v0.4.0/assets/app.js
(() => {
  "use strict";

  const translations = {
    zh: {
      topline:"欧洲数字证据,连接中国版权服务",statusOnline:"公开验证已连接",
      navHome:"首页",navCreate:"创建证据",navVerify:"验证证书",navDossier:"中欧档案",navPartner:"合作伙伴",
      heroTitle:"别让你的创意,<em>成为别人的生意。</em>",
      heroLead:"设计、文件和数字内容在发送给工厂、客户或合作伙伴之前,先建立一套可验证的跨境证据。",
      heroProtect:"立即保护",heroVerify:"验证证书",trustSha:"SHA-256 + SM3 本地指纹",trustEu:"欧洲时间戳",trustChina:"中国服务接口已预留",
      verified:"已验证",confirmed:"已确认",demoTitle:"跨境设计系列",levelEu:"欧洲证据",levelChina:"中国证据",connectorReady:"接口已准备",
      available:"可用",apiIntegration:"API 接入",pilot:"试点",
      proofEuTitle:"欧洲数字证据",proofEuText:"文件指纹、可验证证书、二维码、公开验证和完整技术记录。",
      proofBtcTitle:"比特币时间证明",proofBtcText:"通过 OpenTimestamps 将文件指纹锚定到比特币网络。",
      proofChinaTitle:"中国可信时间戳",proofChinaText:"为中国 TSA/API 预留独立连接层,仅传输哈希。",
      proofDossierTitle:"中欧跨境档案",proofDossierText:"在同一页面连接证据、版本、接收方与交付记录。",
      pratoKicker:"PRATO · MODA · TESSILE",pratoTitle:"设计发给工厂之前,先把证据锁定。",
      pratoText:"CopyrightBridge 为纺织、时尚、皮具和产品设计提供一个明确的操作流程:先锁定文件和版本,再发送给生产商、实验室、客户或合作伙伴。",
      startDossier:"开始创建档案",case1:"面料图案与花型",case1Text:"印花、纹理、图案、色彩方案",
      case2:"服装设计与系列",case2Text:"草图、款式、样衣、系列文件",case3:"产品图片与目录",case3Text:"摄影、产品页、电商素材、目录",
      case4:"技术文件与包装",case4Text:"规格书、版型、标签、包装设计",
      createKicker:"先保护,再发送",createTitle:"创建一份跨境证据草案",
      createIntro:"文件始终留在你的浏览器中。CopyrightBridge 本地计算 SHA-256 和 SM3,生成可交给 CopyrightChain 完成正式认证的操作草案。",
      chooseFile:"选择文件",filePrivate:"文件不会上传到本页面",workType:"作品类型",typeTextile:"面料图案 / 花型",typeFashion:"服装设计 / 系列",
      typePhoto:"摄影 / 产品图片",typeCatalog:"目录 / 电商内容",typePackaging:"包装 / 品牌设计",typeSoftware:"软件 / 技术文件",typeOther:"其他创作",
      workTitle:"作品标题",rightsHolder:"权利人 / 企业",recipient:"接收方",destination:"目的地 / 市场",projectRef:"项目 / 订单编号",
      sendReason:"发送目的",reasonProduction:"生产 / 打样",reasonProposal:"商业提案",reasonReview:"客户审核",reasonLicense:"许可 / 授权",reasonOther:"其他",
      notes:"备注",localOnlyTitle:"本地处理",localOnlyText:"哈希在浏览器中计算,原始文件不会发送到 CopyrightBridge。",
      generateDraft:"生成证据草案",clear:"清除",draftAwaiting:"等待创建档案",draftAwaitingText:"选择文件并填写项目数据,即可生成本地跨境证据草案。",
      localDraft:"本地草案 · 尚未认证",certifyNow:"在 CopyrightChain 完成认证",downloadDraft:"下载草案 JSON",
      verifyKicker:"公开验证",verifyTitle:"验证 CopyrightChain 证书",verifyIntro:"输入证书 ID、SHA-256、验证参数或完整验证链接。",verifyButton:"立即验证",
      dossierTitle:"一份作品,一套跨境证据。",dossierText:"一个二维码连接欧洲数字证据、比特币时间证明、中国服务状态、版本历史与交付记录。",
      feature1:"双语作品信息",feature2:"公开验证",feature3:"版本与交付记录",feature4:"中国服务连接层",shareWechat:"复制链接到微信",scanVerify:"扫码打开验证入口",
      partnerKicker:"中国技术合作伙伴",partnerTitle:"把中国可信版权服务接入欧洲市场。",
      partnerText:"CopyrightBridge 正在对接中国时间戳、版权登记、电子证据和侵权监测服务,建设中欧数字版权证据网关。",
      contactPartner:"联系合作团队",partnerApi:"中国 TSA / API 时间戳",partnerRegistration:"版权登记与证书验证",partnerMarket:"欧洲客户入口与双语档案",
      footerText:"连接欧洲数字证据与中国版权服务的独立跨境平台。",
      verificationState:"证书状态",identity:"作品信息",title:"作品标题",owner:"权利人",date:"认证日期",type:"证书类型",layers:"证据层",
      ccEvidence:"CopyrightChain 证据",bitcoin:"比特币 / OpenTimestamps",timestamp:"欧洲时间戳",chinaConnector:"中国证据连接器",
      absent:"此证书未包含",block:"区块",provider:"服务商",readyPartner:"已准备接入合作伙伴 API",invalid:"未找到可验证证书",loading:"正在验证…",
      fileRequired:"请先选择文件。",titleRequired:"请输入作品标题。",hashing:"正在本地计算 SHA-256 和 SM3…",copied:"链接已复制,可粘贴到微信。",copyFailed:"无法自动复制,请复制浏览器地址。",
      draftType:"作品类型",draftDestination:"目的地",draftCreated:"创建时间"
    },
    en: {
      topline:"European digital evidence connected to Chinese copyright services",statusOnline:"Public verification connected",
      navHome:"Home",navCreate:"Create evidence",navVerify:"Verify certificate",navDossier:"China–Europe dossier",navPartner:"Partners",
      heroTitle:"Do not let your creativity <em>become someone else’s business.</em>",
      heroLead:"Before sending designs, files or digital content to a factory, client or partner, create a verifiable cross-border evidence dossier.",
      heroProtect:"Protect now",heroVerify:"Verify certificate",trustSha:"Local SHA-256 + SM3 fingerprints",trustEu:"European timestamp",trustChina:"China service layer prepared",
      verified:"Verified",confirmed:"Confirmed",demoTitle:"Cross-Border Design Collection",levelEu:"European Evidence",levelChina:"China Evidence",connectorReady:"Connector ready",
      available:"AVAILABLE",apiIntegration:"API INTEGRATION",pilot:"PILOT",
      proofEuTitle:"European digital evidence",proofEuText:"File fingerprint, verifiable certificate, QR, public verification and technical record.",
      proofBtcTitle:"Bitcoin time proof",proofBtcText:"OpenTimestamps anchors the file fingerprint to the Bitcoin network.",
      proofChinaTitle:"China trusted timestamp",proofChinaText:"A separate connector for Chinese TSA/API services, transmitting only the hash.",
      proofDossierTitle:"China–Europe dossier",proofDossierText:"Connect evidence, versions, recipients and delivery records on one page.",
      pratoKicker:"PRATO · FASHION · TEXTILE",pratoTitle:"Lock the evidence before the design reaches the factory.",
      pratoText:"CopyrightBridge gives textile, fashion, leather and product-design businesses a clear workflow: lock the file and version first, then send it to producers, laboratories, clients or partners.",
      startDossier:"Start the dossier",case1:"Textile patterns and prints",case1Text:"Prints, textures, patterns and colourways",
      case2:"Fashion designs and collections",case2Text:"Sketches, styles, samples and collection files",case3:"Product images and catalogues",case3Text:"Photography, product pages, e-commerce assets and catalogues",
      case4:"Technical files and packaging",case4Text:"Specifications, patterns, labels and packaging design",
      createKicker:"PROTECT FIRST. SEND SECOND.",createTitle:"Create a cross-border evidence draft",
      createIntro:"The file stays in your browser. CopyrightBridge calculates SHA-256 and SM3 locally and creates an operational draft ready to be certified through CopyrightChain.",
      chooseFile:"Choose file",filePrivate:"The file is not uploaded to this page",workType:"Work type",typeTextile:"Textile pattern / print",typeFashion:"Fashion design / collection",
      typePhoto:"Photography / product image",typeCatalog:"Catalogue / e-commerce content",typePackaging:"Packaging / brand design",typeSoftware:"Software / technical file",typeOther:"Other creative work",
      workTitle:"Work title",rightsHolder:"Rights holder / company",recipient:"Recipient",destination:"Destination / market",projectRef:"Project / order reference",
      sendReason:"Purpose of sending",reasonProduction:"Production / sampling",reasonProposal:"Commercial proposal",reasonReview:"Client review",reasonLicense:"Licence / authorisation",reasonOther:"Other",
      notes:"Notes",localOnlyTitle:"LOCAL PROCESSING",localOnlyText:"Hashes are calculated in the browser. The original file is not sent to CopyrightBridge.",
      generateDraft:"Generate evidence draft",clear:"Clear",draftAwaiting:"Waiting for a dossier",draftAwaitingText:"Choose a file and enter the project data to create a local cross-border evidence draft.",
      localDraft:"LOCAL DRAFT · NOT YET CERTIFIED",certifyNow:"Complete certification on CopyrightChain",downloadDraft:"Download draft JSON",
      verifyKicker:"PUBLIC VERIFICATION",verifyTitle:"Verify a CopyrightChain certificate",verifyIntro:"Enter a certificate ID, SHA-256, verification parameters or a full verification URL.",verifyButton:"Verify now",
      dossierTitle:"One work. One cross-border evidence dossier.",dossierText:"One QR connects European digital evidence, Bitcoin time proof, China service status, version history and delivery records.",
      feature1:"Bilingual work data",feature2:"Public verification",feature3:"Version and delivery records",feature4:"China service connector",shareWechat:"Copy link for WeChat",scanVerify:"Scan to open verification",
      partnerKicker:"CHINESE TECHNOLOGY PARTNERS",partnerTitle:"Bring trusted Chinese copyright services into the European market.",
      partnerText:"CopyrightBridge is connecting Chinese timestamp, copyright registration, electronic evidence and infringement-monitoring services to build a China–Europe digital copyright gateway.",
      contactPartner:"Contact partnership team",partnerApi:"Chinese TSA / API timestamp",partnerRegistration:"Copyright registration and certificate verification",partnerMarket:"European client gateway and bilingual dossier",
      footerText:"An independent cross-border platform connecting European digital evidence and Chinese copyright services.",
      verificationState:"CERTIFICATE STATUS",identity:"Work identity",title:"Title",owner:"Owner",date:"Certification date",type:"Certificate type",layers:"Evidence layers",
      ccEvidence:"CopyrightChain Evidence",bitcoin:"Bitcoin / OpenTimestamps",timestamp:"European Timestamp",chinaConnector:"China Evidence Connector",
      absent:"Not included in this certificate",block:"Block",provider:"Provider",readyPartner:"Ready for partner API",invalid:"No verifiable certificate found",loading:"Verifying…",
      fileRequired:"Choose a file first.",titleRequired:"Enter a work title.",hashing:"Calculating SHA-256 and SM3 locally…",copied:"Link copied. Paste it into WeChat.",copyFailed:"Automatic copy failed. Copy the browser address.",
      draftType:"Work type",draftDestination:"Destination",draftCreated:"Created"
    },
    it: {
      topline:"Prova digitale europea collegata ai servizi cinesi sul copyright",statusOnline:"Verifica pubblica collegata",
      navHome:"Home",navCreate:"Crea una prova",navVerify:"Verifica",navDossier:"Dossier Cina–Europa",navPartner:"Partner",
      heroTitle:"Non lasciare che la tua creatività <em>diventi il business di qualcun altro.</em>",
      heroLead:"Prima di inviare design, file o contenuti digitali a un produttore, cliente o partner, crea un dossier transfrontaliero verificabile.",
      heroProtect:"Proteggi ora",heroVerify:"Verifica certificato",trustSha:"Impronte locali SHA-256 + SM3",trustEu:"Marca temporale europea",trustChina:"Livello servizi Cina predisposto",
      verified:"Verificato",confirmed:"Confermato",demoTitle:"Collezione di design transfrontaliera",levelEu:"Prova europea",levelChina:"Prova cinese",connectorReady:"Connettore pronto",
      available:"DISPONIBILE",apiIntegration:"INTEGRAZIONE API",pilot:"PILOTA",
      proofEuTitle:"Prova digitale europea",proofEuText:"Impronta del file, certificato verificabile, QR, verifica pubblica e registrazione tecnica.",
      proofBtcTitle:"Prova temporale Bitcoin",proofBtcText:"OpenTimestamps ancora l’impronta del file alla rete Bitcoin.",
      proofChinaTitle:"Marca temporale cinese",proofChinaText:"Connettore separato per TSA/API cinesi, trasmettendo soltanto l’hash.",
      proofDossierTitle:"Dossier Cina–Europa",proofDossierText:"Collega prove, versioni, destinatari e registri di consegna in una sola pagina.",
      pratoKicker:"PRATO · MODA · TESSILE",pratoTitle:"Blocca la prova prima che il design arrivi al produttore.",
      pratoText:"CopyrightBridge offre a tessile, moda, pelletteria e design di prodotto un flusso chiaro: prima fissa file e versione, poi invia a produttori, laboratori, clienti o partner.",
      startDossier:"Inizia il dossier",case1:"Pattern e disegni tessili",case1Text:"Stampe, texture, fantasie e varianti colore",
      case2:"Design moda e collezioni",case2Text:"Bozzetti, modelli, campioni e file di collezione",case3:"Immagini prodotto e cataloghi",case3Text:"Fotografia, schede prodotto, e-commerce e cataloghi",
      case4:"File tecnici e packaging",case4Text:"Specifiche, cartamodelli, etichette e confezioni",
      createKicker:"PRIMA PROTEGGI. POI INVIA.",createTitle:"Crea una bozza di prova transfrontaliera",
      createIntro:"Il file resta nel browser. CopyrightBridge calcola localmente SHA-256 e SM3 e genera una bozza operativa pronta per la certificazione su CopyrightChain.",
      chooseFile:"Seleziona il file",filePrivate:"Il file non viene caricato in questa pagina",workType:"Tipo di opera",typeTextile:"Pattern / disegno tessile",typeFashion:"Design moda / collezione",
      typePhoto:"Fotografia / immagine prodotto",typeCatalog:"Catalogo / contenuto e-commerce",typePackaging:"Packaging / identità di marca",typeSoftware:"Software / file tecnico",typeOther:"Altra opera creativa",
      workTitle:"Titolo dell’opera",rightsHolder:"Titolare / impresa",recipient:"Destinatario",destination:"Destinazione / mercato",projectRef:"Progetto / ordine",
      sendReason:"Motivo dell’invio",reasonProduction:"Produzione / campionatura",reasonProposal:"Proposta commerciale",reasonReview:"Revisione cliente",reasonLicense:"Licenza / autorizzazione",reasonOther:"Altro",
      notes:"Note",localOnlyTitle:"ELABORAZIONE LOCALE",localOnlyText:"Gli hash vengono calcolati nel browser. Il file originale non viene inviato a CopyrightBridge.",
      generateDraft:"Genera bozza di prova",clear:"Pulisci",draftAwaiting:"In attesa del dossier",draftAwaitingText:"Seleziona un file e inserisci i dati del progetto per creare una bozza locale di prova transfrontaliera.",
      localDraft:"BOZZA LOCALE · NON ANCORA CERTIFICATA",certifyNow:"Completa la certificazione su CopyrightChain",downloadDraft:"Scarica bozza JSON",
      verifyKicker:"VERIFICA PUBBLICA",verifyTitle:"Verifica un certificato CopyrightChain",verifyIntro:"Inserisci ID, SHA-256, parametri o URL completo di verifica.",verifyButton:"Verifica ora",
      dossierTitle:"Un’opera. Un dossier di prova transfrontaliero.",dossierText:"Un QR collega prova digitale europea, prova temporale Bitcoin, stato dei servizi cinesi, versioni e registri di consegna.",
      feature1:"Dati bilingui dell’opera",feature2:"Verifica pubblica",feature3:"Versioni e consegne",feature4:"Connettore servizi Cina",shareWechat:"Copia link per WeChat",scanVerify:"Scansiona per aprire la verifica",
      partnerKicker:"PARTNER TECNOLOGICI CINESI",partnerTitle:"Porta i servizi fiduciari cinesi nel mercato europeo.",
      partnerText:"CopyrightBridge collega timestamp, registrazione del copyright, prova elettronica e monitoraggio cinesi per costruire il gateway digitale Cina–Europa.",
      contactPartner:"Contatta il team partnership",partnerApi:"Timestamp cinese TSA / API",partnerRegistration:"Registrazione e verifica del copyright",partnerMarket:"Ingresso clienti europei e dossier bilingue",
      footerText:"Piattaforma transfrontaliera indipendente che collega prove digitali europee e servizi cinesi sul copyright.",
      verificationState:"STATO CERTIFICATO",identity:"Identità dell’opera",title:"Titolo",owner:"Titolare",date:"Data di certificazione",type:"Tipo certificato",layers:"Livelli di prova",
      ccEvidence:"Prova CopyrightChain",bitcoin:"Bitcoin / OpenTimestamps",timestamp:"Marca temporale europea",chinaConnector:"Connettore prova cinese",
      absent:"Non presente in questo certificato",block:"Blocco",provider:"Provider",readyPartner:"Pronto per API partner",invalid:"Certificato verificabile non trovato",loading:"Verifica in corso…",
      fileRequired:"Seleziona prima un file.",titleRequired:"Inserisci il titolo dell’opera.",hashing:"Calcolo locale SHA-256 e SM3 in corso…",copied:"Link copiato. Puoi incollarlo su WeChat.",copyFailed:"Copia automatica non riuscita. Copia l’indirizzo del browser.",
      draftType:"Tipo opera",draftDestination:"Destinazione",draftCreated:"Creato"
    }
  };

  let currentLang = localStorage.getItem("cb-lang") || "zh";
  let currentDraft = null;

  const q = (s, root=document) => root.querySelector(s);
  const qa = (s, root=document) => [...root.querySelectorAll(s)];
  const t = key => translations[currentLang]?.[key] ?? translations.en[key] ?? key;

  function applyLanguage(lang){
    currentLang = translations[lang] ? lang : "zh";
    localStorage.setItem("cb-lang", currentLang);
    document.documentElement.lang = currentLang === "zh" ? "zh-CN" : currentLang;
    qa("[data-lang]").forEach(b => b.classList.toggle("active", b.dataset.lang === currentLang));
    qa("[data-i18n]").forEach(el => {
      const value = t(el.dataset.i18n);
      if (value !== undefined) el.innerHTML = value;
    });
    if (currentDraft) renderDraft(currentDraft);
  }

  qa("[data-lang]").forEach(btn => btn.addEventListener("click", () => applyLanguage(btn.dataset.lang)));
  q("#mobileMenu").addEventListener("click", () => {
    const nav = q("#mainNav");
    nav.classList.toggle("open");
    q("#mobileMenu").setAttribute("aria-expanded", nav.classList.contains("open"));
  });
  qa("#mainNav a").forEach(a => a.addEventListener("click", () => q("#mainNav").classList.remove("open")));

  const fileInput = q("#evidenceFile");
  const fileDrop = q("#fileDrop");
  fileInput.addEventListener("change", () => updateFileName());
  ["dragenter","dragover"].forEach(ev => fileDrop.addEventListener(ev, e => {e.preventDefault();fileDrop.classList.add("drag")}));
  ["dragleave","drop"].forEach(ev => fileDrop.addEventListener(ev, e => {e.preventDefault();fileDrop.classList.remove("drag")}));
  fileDrop.addEventListener("drop", e => {
    if (e.dataTransfer.files?.length) {
      fileInput.files = e.dataTransfer.files;
      updateFileName();
    }
  });
  function updateFileName(){
    const f = fileInput.files?.[0];
    q("#fileName").textContent = f ? `${f.name} · ${formatBytes(f.size)}` : t("filePrivate");
  }
  function formatBytes(n){
    if (!Number.isFinite(n)) return "—";
    const u=["B","KB","MB","GB"]; let i=0,v=n;
    while(v>=1024&&i<u.length-1){v/=1024;i++}
    return `${v.toFixed(i?1:0)} ${u[i]}`;
  }

  function rotr(x,n){ return (x>>>n)|(x<<(32-n)); }
  function rotl(x,n){ return (x<<n)|(x>>>(32-n)); }
  function p0(x){ return x ^ rotl(x,9) ^ rotl(x,17); }
  function p1(x){ return x ^ rotl(x,15) ^ rotl(x,23); }
  function ff(x,y,z,j){ return j<16 ? x^y^z : (x&y)|(x&z)|(y&z); }
  function gg(x,y,z,j){ return j<16 ? x^y^z : (x&y)|((~x)&z); }

  function sm3Hex(input){
    const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
    const bitLen = bytes.length * 8;
    const padLen = (56 - ((bytes.length + 1) % 64) + 64) % 64;
    const padded = new Uint8Array(bytes.length + 1 + padLen + 8);
    padded.set(bytes); padded[bytes.length] = 0x80;
    const view = new DataView(padded.buffer);
    const hi = Math.floor(bitLen / 0x100000000);
    const lo = bitLen >>> 0;
    view.setUint32(padded.length-8, hi, false);
    view.setUint32(padded.length-4, lo, false);

    let V = [0x7380166f,0x4914b2b9,0x172442d7,0xda8a0600,0xa96f30bc,0x163138aa,0xe38dee4d,0xb0fb0e4e];
    const W = new Uint32Array(68), W1 = new Uint32Array(64);
    for(let off=0; off<padded.length; off+=64){
      for(let j=0;j<16;j++) W[j]=view.getUint32(off+j*4,false);
      for(let j=16;j<68;j++) W[j]=(p1(W[j-16]^W[j-9]^rotl(W[j-3],15))^rotl(W[j-13],7)^W[j-6])>>>0;
      for(let j=0;j<64;j++) W1[j]=(W[j]^W[j+4])>>>0;
      let [A,B,C,D,E,F,G,H]=V;
      for(let j=0;j<64;j++){
        const T = j<16 ? 0x79cc4519 : 0x7a879d8a;
        const SS1 = rotl(((rotl(A,12) + E + rotl(T,j%32))>>>0),7)>>>0;
        const SS2 = (SS1 ^ rotl(A,12))>>>0;
        const TT1 = (ff(A,B,C,j) + D + SS2 + W1[j])>>>0;
        const TT2 = (gg(E,F,G,j) + H + SS1 + W[j])>>>0;
        D=C; C=rotl(B,9)>>>0; B=A; A=TT1;
        H=G; G=rotl(F,19)>>>0; F=E; E=p0(TT2)>>>0;
      }
      V=[A^V[0],B^V[1],C^V[2],D^V[3],E^V[4],F^V[5],G^V[6],H^V[7]].map(x=>x>>>0);
    }
    return V.map(x=>x.toString(16).padStart(8,"0")).join("");
  }

  async function sha256Hex(buffer){
    const digest = await crypto.subtle.digest("SHA-256", buffer);
    return [...new Uint8Array(digest)].map(b=>b.toString(16).padStart(2,"0")).join("");
  }

  q("#evidenceForm").addEventListener("submit", async e => {
    e.preventDefault();
    const file = fileInput.files?.[0];
    const title = q("#workTitle").value.trim();
    if (!file) return showDraftError(t("fileRequired"));
    if (!title) return showDraftError(t("titleRequired"));
    q("#generateDraft").disabled = true;
    q("#generateDraft").textContent = t("hashing");
    try {
      const buffer = await file.arrayBuffer();
      const [sha, sm3] = await Promise.all([sha256Hex(buffer), Promise.resolve(sm3Hex(buffer))]);
      currentDraft = {
        draft_id: `CB-DRAFT-${Date.now()}`,
        created_at: new Date().toISOString(),
        file_name: file.name,
        file_size: file.size,
        work_type: q("#workType").value,
        title,
        rights_holder: q("#rightsHolder").value.trim(),
        recipient: q("#recipient").value.trim(),
        destination: q("#destination").value,
        project_reference: q("#projectRef").value.trim(),
        send_reason: q("#sendReason").value,
        notes: q("#notes").value.trim(),
        sha256: sha,
        sm3
      };
      localStorage.setItem("cb-last-draft", JSON.stringify(currentDraft));
      renderDraft(currentDraft);
    } catch (err) {
      showDraftError(String(err?.message || err));
    } finally {
      q("#generateDraft").disabled = false;
      q("#generateDraft").innerHTML = t("generateDraft");
    }
  });

  function renderDraft(d){
    q("#draftEmpty").hidden = true;
    q("#draftContent").hidden = false;
    q("#draftTitle").textContent = d.title || "—";
    const typeLabel = q(`#workType option[value="${CSS.escape(d.work_type)}"]`)?.textContent || d.work_type;
    q("#draftMeta").textContent = `${typeLabel} · ${d.file_name} · ${formatBytes(d.file_size)}`;
    q("#draftSha").textContent = d.sha256;
    q("#draftSm3").textContent = d.sm3;
    q("#draftRecipient").textContent = d.recipient || "—";
    q("#draftProject").textContent = d.project_reference || "—";
  }
  function showDraftError(msg){ alert(msg); }
  q("#clearDraft").addEventListener("click", () => {
    q("#evidenceForm").reset(); updateFileName(); currentDraft=null;
    q("#draftContent").hidden=true; q("#draftEmpty").hidden=false;
  });
  q("#downloadDraft").addEventListener("click", () => {
    if(!currentDraft) return;
    const blob = new Blob([JSON.stringify(currentDraft,null,2)],{type:"application/json"});
    const a=document.createElement("a"); a.href=URL.createObjectURL(blob);
    a.download=`${currentDraft.draft_id}.json`; a.click(); URL.revokeObjectURL(a.href);
  });

  function parseVerifyInput(raw){
    const input = raw.trim(), params = {};
    if(!input) return params;
    try{
      if(/^https?:\/\//i.test(input)){
        const u=new URL(input);
        ["id","hash","txid","code"].forEach(k=>{const v=u.searchParams.get(k);if(v)params[k]=v});
      }else if(input.includes("=")||input.includes("&")){
        let parts=input.replace(/^\?/,"").split("&");
        if(parts[0]&&!parts[0].includes("=")){
          const first=parts.shift(); params.id=/^\d{6}$/.test(first)?`CD-${first}`:first;
        }
        const sp=new URLSearchParams(parts.join("&"));
        ["id","hash","txid","code"].forEach(k=>{const v=sp.get(k);if(v)params[k]=v});
      }else if(/^[a-f0-9]{64}$/i.test(input)) params.hash=input;
      else params.id=/^\d{6}$/.test(input)?`CD-${input}`:input;
    }catch{params.id=input}
    return params;
  }
  function normalizeCertificate(data){
    const x=data?.certificate??data?.data??data??{};
    if(x.ok===false||x.verified===false) throw new Error(x.error||"VERIFY_NOT_FOUND");
    const id=x.certificate_id??x.cert_id??x.id??x.uid??"—";
    let title=x.title??x.filename??x.work_title??`CopyrightChain ${id}`;
    if(/^Copyright_Cert_CD-\d+$/i.test(title)&&!title.includes(id)) title=`CopyrightChain ${id}`;
    const bitcoinPresent=!!(x.btc_txid||x.btc_block_height||x.blockchain_status||x.ots_status||x.ots_proof_url||x.confirmed_at||x.anchored_at);
    const timestampPresent=!!(x.timestamp_used||x.timestamp_available||x.timestamp_status||x.timestamp_provider||x.timestamp_issued_at);
    return {
      id,title,owner:x.owner??x.holder??x.author??x.full_name??"—",
      date:x.created_at??x.certified_at??x.deposit_date??x.timestamp??"—",
      hash:x.sha256??x.hash??x.file_hash??"—",type:x.cert_type??"—",
      bitcoinPresent,bitcoinStatus:x.bitcoin_status??x.blockchain_status??x.ots_status??x.anchor_status??"",
      block:x.btc_block_height??x.block_height??x.ots_block_height??"",
      timestampPresent,timestampStatus:x.timestamp_status??"",timestampProvider:x.timestamp_provider??""
    };
  }
  async function verifyCertificate(raw){
    if(raw.trim().toUpperCase()==="CC-CB-DEMO-2026") return {
      id:"CC-CB-DEMO-2026",title:t("demoTitle"),owner:"CopyrightBridge Demo",date:new Date().toISOString(),
      hash:"8e4e2b9d0f3bd8f0baf03f4522ca2f0612da23e43f3d4a284b0b9a9a9d7a41",type:"demo",
      bitcoinPresent:true,bitcoinStatus:"confirmed",block:"949491",timestampPresent:true,timestampStatus:"issued",timestampProvider:"Openapi / InfoCert"
    };
    const params=new URLSearchParams(parseVerifyInput(raw));
    if(![...params].length) throw new Error("MISSING_QUERY");
    const res=await fetch(`/cc-api/api/ai/verify/public?${params.toString()}`,{headers:{Accept:"application/json"}});
    if(!res.ok) throw new Error(`VERIFY_HTTP_${res.status}`);
    return normalizeCertificate(await res.json());
  }
  function fmtDate(v){
    if(!v||v==="—")return "—";
    const d=new Date(v); if(Number.isNaN(d.getTime())) return v;
    return d.toLocaleString(currentLang==="zh"?"zh-CN":currentLang==="it"?"it-IT":"en-GB");
  }
  function dataRow(label,value,mono=false){
    return `<div class="data-row"><span>${escapeHtml(label)}</span>${mono?`<code>${escapeHtml(String(value))}</code>`:`<b>${escapeHtml(String(value))}</b>`}</div>`;
  }
  function evidenceRow(label,status,detail,pending=false){
    return `<div class="evidence-row ${pending?"pending":""}"><span class="evidence-symbol">${pending?"↗":"✓"}</span><div><b>${escapeHtml(label)}</b><small>${escapeHtml(status)}${detail?` · ${escapeHtml(detail)}`:""}</small></div></div>`;
  }
  function renderVerification(c){
    const btcStatus=c.bitcoinPresent?(c.bitcoinStatus||t("verified")):t("absent");
    const btcDetail=c.bitcoinPresent&&c.block?`${t("block")} ${c.block}`:"";
    const tsStatus=c.timestampPresent?(c.timestampStatus||t("verified")):t("absent");
    const tsDetail=c.timestampPresent&&c.timestampProvider?`${t("provider")}: ${c.timestampProvider}`:"";
    q("#verifyResult").innerHTML=`
      <div class="verified-banner">
        <span class="verified-icon">✓</span>
        <div><small>${escapeHtml(t("verificationState"))}</small><h3>${escapeHtml(t("verified"))}</h3></div>
        <code>${escapeHtml(c.id)}</code>
      </div>
      <div class="verification-grid">
        <div class="result-panel"><h3>${escapeHtml(t("identity"))}</h3>
          ${dataRow(t("title"),c.title)}
          ${dataRow(t("owner"),c.owner)}
          ${dataRow(t("date"),fmtDate(c.date))}
          ${dataRow(t("type"),c.type)}
          ${dataRow("SHA-256",c.hash,true)}
        </div>
        <div class="result-panel"><h3>${escapeHtml(t("layers"))}</h3>
          ${evidenceRow(t("ccEvidence"),t("verified"),"")}
          ${evidenceRow(t("bitcoin"),btcStatus,btcDetail,!c.bitcoinPresent)}
          ${evidenceRow(t("timestamp"),tsStatus,tsDetail,!c.timestampPresent)}
          ${evidenceRow(t("chinaConnector"),t("readyPartner"),"",true)}
        </div>
      </div>`;
    q("#verifyResult").hidden=false;
  }
  async function runVerify(){
    const raw=q("#verifyQuery").value;
    const msg=q("#verifyMessage");
    msg.hidden=false; msg.className="verify-message loading"; msg.textContent=t("loading");
    q("#verifyResult").hidden=true;
    try{
      const result=await verifyCertificate(raw);
      msg.hidden=true; renderVerification(result);
    }catch(err){
      msg.className="verify-message";msg.textContent=`${t("invalid")} (${err?.message||err})`;
    }
  }
  q("#verifyButton").addEventListener("click",runVerify);
  q("#verifyQuery").addEventListener("keydown",e=>{if(e.key==="Enter")runVerify()});
  q("#demoVerify").addEventListener("click",()=>{q("#verifyQuery").value="CC-CB-DEMO-2026";runVerify()});

  q("#shareWechat").addEventListener("click",async()=>{
    const value=`${location.origin}${location.pathname}#verify`;
    try{await navigator.clipboard.writeText(value);q("#shareStatus").textContent=t("copied")}
    catch{q("#shareStatus").textContent=t("copyFailed")}
  });

  function escapeHtml(v){
    return String(v??"").replace(/[&<>"']/g,m=>({"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#039;"}[m]));
  }

  const saved=localStorage.getItem("cb-last-draft");
  if(saved){try{currentDraft=JSON.parse(saved);renderDraft(currentDraft)}catch{}}
  applyLanguage(currentLang);

  // SM3 known test vector: "abc" must equal 66c7f0f462eeedd9...
  const test=sm3Hex(new TextEncoder().encode("abc"));
  if(!test.startsWith("66c7f0f462eeedd9")) console.warn("SM3 self-test failed",test);
})();

Youez - 2016 - github.com/yon3zu
LinuXploit