1import {AsyncActionButton} from "@/components/button/AsyncActionButton.mjs" 2import {html} from 'htm/preact'; 3import {css} from '@/cssTool/css.mjs'; 4import {useState,useEffect,useCallback,useRef} from 'preact/hooks'; 5import {Config} from "@/config/config.mjs"; 6 7 8 9 10function randomString(length){ 11 const alphabet="AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz" 12 return Array.from({length}, () => alphabet[Math.floor(Math.random() * alphabet.length)]).join(''); 13} 14 15 16export function TurnStileConfirmButton( 17 { 18 onClick, 19 disabled = false, 20 children, 21 preTurnStileOnClick, 22 ...props 23 } 24){ 25 26 const containerRef=useRef(null); 27 const [containerClass,setContainerClass]=useState("turnstile-container-"+randomString(25)); 28 const widgetId=useRef(null); 29 const [isRunning,setIsRunning]=useState(false); 30 const pendingPromiseRef=useRef(null); 31 32 33 34 useEffect(()=>{ 35 let load=async ()=> { 36 37 for(let i=0;i<200;i++){ 38 if(window.turnstile){ 39 break; 40 } 41 await new Promise(resolve=>setTimeout(resolve,100)); 42 } 43 const config=await Config.getConfig(); 44 45 const wid = window.turnstile.render("#" + containerClass, { 46 sitekey: config.turnstileSiteKey, 47 execution: "execute", 48 appearance: "execute", 49 callback: async (token) => { 50 console.log("Received token") 51 const pending = pendingPromiseRef.current; 52 pendingPromiseRef.current = null; 53 window.turnstile.reset(widgetId.current); 54 setIsRunning(false); 55 pending?.resolve(token); 56 57 }, 58 "error-callback": (err) => { 59 console.log("Turnstile error", err) 60 const pending = pendingPromiseRef.current; 61 pendingPromiseRef.current = null; 62 window.turnstile.reset(widgetId.current); 63 setIsRunning(false); 64 pending?.reject(new Error("Turnstile error " + err)); 65 }, 66 "timeout-callback": () => { 67 console.log("Turnstile timeout") 68 const pending = pendingPromiseRef.current; 69 pendingPromiseRef.current = null; 70 window.turnstile.reset(widgetId.current); 71 setIsRunning(false); 72 pending?.reject(new Error("Turnstile timeout")); 73 } 74 75 } 76 ) 77 widgetId.current=wid; 78 } 79 load().then(); 80 81 82 return ()=>{ 83 if(widgetId.current!==null){ 84 window.turnstile.remove(widgetId.current); 85 widgetId.current=null; 86 } 87 } 88 89 },[]) 90 91 const getTokenFromTurnstile=useCallback( 92 ()=>{ 93 return new Promise((resolve,reject)=>{ 94 if(widgetId.current===null){ 95 reject(new Error("Turnstile widget not initialized")); 96 return; 97 } 98 if(isRunning){ 99 reject(new Error("Turnstile already running")); 100 return; 101 } 102 setIsRunning(true); 103 pendingPromiseRef.current={resolve,reject}; 104 try { 105 window.turnstile.execute(widgetId.current); 106 }catch(err){ 107 setIsRunning(false); 108 pendingPromiseRef.current=null; 109 reject(err); 110 } 111 } 112 ) 113 } 114 115 ,[ 116 isRunning, 117 setIsRunning, 118 widgetId, 119 pendingPromiseRef, 120 onClick 121 ] 122 ) 123 124 const onClickWithTurnstile=useCallback( 125 async()=>{
126 const preTurnStileResult=await preTurnStileOnClick?.(); 127 const token=await getTokenFromTurnstile(); 128 await onClick?.(token,preTurnStileResult); 129 },[ 130 onClick, 131 getTokenFromTurnstile, 132 preTurnStileOnClick 133 ] 134 ) 135 136 137 return html` 138 <div className="d-flex flex-column gap-2"> 139 <${AsyncActionButton} 140 onClick=${onClickWithTurnstile} 141 disabled=${disabled} 142 ...${props} 143 > 144 ${children} 145 <//> 146 <div ref=${containerRef} id=${containerClass}></div> 147 </div> 148 ` 149}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.