@@ -13,6 +13,7 @@ let animationStart
1313let courses
1414let actions = "fmt,ui,eval"
1515let editor
16+ let errors = false
1617// --- Initialise ------------------------------------------------------
1718
1819initWasm ( )
@@ -27,7 +28,7 @@ function initWasm() {
2728 . then ( ( obj ) => ( wasmModule = obj ) )
2829 . catch ( ( err ) => console . error ( err ) )
2930 const runButton = document . querySelector ( "#run" )
30- const runButtonMob = document . querySelector ( "#run-mob " )
31+ const runButtonMob = document . querySelector ( "#run-mobile " )
3132 runButton . onclick = handleRun
3233 runButton . disabled = false
3334 runButtonMob . onclick = handleMobRun
@@ -110,6 +111,7 @@ function jsRead() {
110111}
111112
112113function jsError ( ptr , len ) {
114+ errors = true
113115 const code = editor . value
114116 const lines = code . split ( "\n" )
115117 const errs = memToString ( ptr , len ) . split ( "\n" )
@@ -185,7 +187,7 @@ async function handleMobRun() {
185187 }
186188 // on output screen
187189 if ( stopped ) {
188- const runButtonMob = document . querySelector ( "#run-mob " )
190+ const runButtonMob = document . querySelector ( "#run-mobile " )
189191 runButtonMob . innerText = "Run"
190192 slide ( )
191193 return
@@ -197,11 +199,12 @@ async function handleMobRun() {
197199// code and initialises the output ui.
198200async function start ( ) {
199201 stopped = false
202+ errors = false
200203 wasmInst = await WebAssembly . instantiate ( wasmModule , go . importObject )
201204 clearOutput ( )
202205
203206 const runButton = document . querySelector ( "#run" )
204- const runButtonMob = document . querySelector ( "#run-mob " )
207+ const runButtonMob = document . querySelector ( "#run-mobile " )
205208 runButton . innerText = "Stop"
206209 runButton . classList . add ( "running" )
207210 runButtonMob . innerText = "Stop"
@@ -212,6 +215,8 @@ async function start() {
212215
213216// format calls evy wasm/go main() but doesn't evaluate.
214217async function format ( ) {
218+ stopped = false
219+ errors = false
215220 wasmInst = await WebAssembly . instantiate ( wasmModule , go . importObject )
216221 actions = "fmt,ui"
217222 go . run ( wasmInst )
@@ -234,7 +239,7 @@ function afterStop() {
234239 wasmInst = undefined
235240
236241 const runButton = document . querySelector ( "#run" )
237- const runButtonMob = document . querySelector ( "#run-mob " )
242+ const runButtonMob = document . querySelector ( "#run-mobile " )
238243 runButton . classList . remove ( "running" )
239244 runButton . innerText = "Run"
240245 runButtonMob . classList . remove ( "running" )
@@ -276,6 +281,7 @@ async function initUI() {
276281 await fetchCourses ( )
277282 window . addEventListener ( "hashchange" , handleHashChange )
278283 document . querySelector ( "#modal-close" ) . onclick = hideModal
284+ document . querySelector ( "#share" ) . onclick = share
279285 initModal ( )
280286 handleHashChange ( )
281287 initEditor ( )
@@ -305,9 +311,16 @@ async function handleHashChange() {
305311 hideModal ( )
306312 await stopAndSlide ( ) // go to code screen for new code
307313 let opts = parseHash ( )
308- if ( ! opts . source && ! opts . unit ) {
314+ if ( ! opts . source && ! opts . unit && ! opts . content ) {
309315 opts = { unit : "welcome" }
310316 }
317+ if ( opts . content ) {
318+ console . log ( "start content update" )
319+ const decoded = await decode ( opts . content )
320+ editor . update ( { value : decoded , errorLines : { } } )
321+ console . log ( "finished content update" )
322+ return
323+ }
311324 let crumbs = [ "Evy" ]
312325 if ( opts . unit ) {
313326 const unit = courses . units [ opts . unit ]
@@ -498,7 +511,7 @@ function registerEventHandler(ptr, len) {
498511
499512function unfocusRunBotton ( ) {
500513 const runButton = document . querySelector ( "#run" )
501- const runButtonMob = document . querySelector ( "#run-mob " )
514+ const runButtonMob = document . querySelector ( "#run-mobile " )
502515 document . activeElement === runButton && runButton . blur ( )
503516 document . activeElement === runButtonMob && runButtonMob . blur ( )
504517}
@@ -575,9 +588,22 @@ function hideModal() {
575588 el . classList . add ( "hidden" )
576589}
577590
578- function showModal ( ) {
579- const el = document . querySelector ( "#modal" )
580- el . classList . remove ( "hidden" )
591+ function showCourses ( ) {
592+ const courses = document . querySelector ( "#modal-courses" )
593+ courses . classList . remove ( "hidden" )
594+ const share = document . querySelector ( "#modal-share" )
595+ share . classList . add ( "hidden" )
596+ const modal = document . querySelector ( "#modal" )
597+ modal . classList . remove ( "hidden" )
598+ }
599+
600+ function showSharing ( ) {
601+ const share = document . querySelector ( "#modal-share" )
602+ share . classList . remove ( "hidden" )
603+ const courses = document . querySelector ( "#modal-courses" )
604+ courses . classList . add ( "hidden" )
605+ const modal = document . querySelector ( "#modal" )
606+ modal . classList . remove ( "hidden" )
581607}
582608
583609function updateBreadcrumbs ( crumbs ) {
@@ -589,7 +615,7 @@ function updateBreadcrumbs(crumbs) {
589615function breadcrumb ( s ) {
590616 const btn = document . createElement ( "button" )
591617 btn . textContent = s
592- btn . onclick = ( ) => showModal ( )
618+ btn . onclick = ( ) => showCourses ( )
593619 const li = document . createElement ( "li" )
594620 li . appendChild ( btn )
595621 return li
@@ -650,6 +676,91 @@ function showConfetti() {
650676 } , 8500 )
651677}
652678
679+ // --- Share / load snippets -------------------------------------------
680+
681+ async function share ( ) {
682+ console . log ( "share" )
683+ await format ( )
684+ const el = document . querySelector ( "#modal-share" )
685+
686+ if ( errors ) {
687+ const msg = document . createElement ( "label" )
688+ msg . textContent = "Fix errors first please."
689+ const button = document . createElement ( "button" )
690+ button . innerText = "OK"
691+ button . onclick = hideModal
692+ el . replaceChildren ( msg , button )
693+ showSharing ( )
694+ console . log ( "Fix errors first please." )
695+ return
696+ }
697+ const encoded = await encode ( editor . value )
698+ const msg = document . createElement ( "label" )
699+ msg . textContent = "Share"
700+ const input = document . createElement ( "input" )
701+ input . type = "text"
702+ input . onclick = input . select
703+ const baseurl = window . location . origin + window . location . pathname
704+ input . value = `${ baseurl } #content=${ encoded } `
705+ const button = document . createElement ( "button" )
706+ button . className = "copy"
707+ button . innerHTML = `<svg><use href="#icon-copy" /></svg>`
708+ button . onclick = ( ) => {
709+ navigator . clipboard . writeText ( input . value )
710+ hideModal ( )
711+ }
712+ el . replaceChildren ( msg , input , button )
713+ showSharing ( )
714+ console . log ( encoded )
715+ }
716+
717+ async function encode ( input ) {
718+ await polyfillCompression ( )
719+ const buffer = new TextEncoder ( ) . encode ( input )
720+ const stream = readableStream ( buffer ) . pipeThrough ( new CompressionStream ( "gzip" ) )
721+ const compressedBuffer = await bufferFromStream ( stream )
722+ const encoded = btoa ( String . fromCharCode ( ...compressedBuffer ) )
723+ return encoded
724+ }
725+
726+ async function decode ( encoded ) {
727+ await polyfillCompression ( )
728+ const bytes = atob ( encoded ) . split ( "" )
729+ const buffer = new Uint8Array ( bytes . map ( ( b ) => b . charCodeAt ( 0 ) ) )
730+ const stream = readableStream ( buffer ) . pipeThrough ( new DecompressionStream ( "gzip" ) )
731+ const decompressedBuffer = await bufferFromStream ( stream )
732+ const decoded = new TextDecoder ( ) . decode ( decompressedBuffer )
733+ return decoded
734+ }
735+
736+ async function polyfillCompression ( ) {
737+ if ( ! window . CompressionStream ) {
738+ await import ( "https://unpkg.com/compression-streams-polyfill" )
739+ }
740+ }
741+
742+ function readableStream ( buffer ) {
743+ return new ReadableStream ( {
744+ start ( controller ) {
745+ controller . enqueue ( buffer )
746+ controller . close ( )
747+ } ,
748+ } )
749+ }
750+
751+ async function bufferFromStream ( stream ) {
752+ const reader = stream . getReader ( )
753+ let buffer = new Uint8Array ( )
754+ while ( true ) {
755+ const { done, value } = await reader . read ( )
756+ if ( done ) {
757+ break
758+ }
759+ buffer = new Uint8Array ( [ ...buffer , ...value ] )
760+ }
761+ return buffer
762+ }
763+
653764// --- Utilities -------------------------------------------------------
654765
655766function getElements ( q ) {
0 commit comments