Here is a demo of some of the things you can do with an Html5 build
https://godling.itch.io/sv?secret=AGKForum including writing to the clipboard, downloading text, image, or other binary files, reading text, image or other binary files and getting the url including passed parameters of your app. This patch also includes localStorage access. You can find the source for the demo here
https://github.com/charlesgriffiths/agk-modules/tree/main/other/SV
These functions are made possible by patching AGKPlayer.js after exporting your app to Html5 but before renaming the entry to index.html and packaging in a zip file. Nobody wants to patch that file by hand, so I wrote a simple patcher.
https://godling.itch.io/agkplayer-patcher?secret=AGKForum If you ever want to write your own, or just prefer patching your app with code you control (who could blame you), here is the source and the patch.txt attached to this post is the source for the SetPatchString() function in the patcher. The patcher will read either minified or unminified AGKPlayer.js, apply the patch, and immediately download the new version. You can run the patcher as a windows app and it will read and write files in its usual media folder.
#constant debug 0
// Project: AGKPlayerPatcher
// Created: 22-07-29
// show all errors
SetErrorMode(2)
// set window properties
SetWindowTitle( "AGKPlayerPatcher" )
SetWindowSize( 1024, 768, 0 )
SetWindowAllowResize( 1 ) // allow the user to resize the window
// set display properties
SetVirtualResolution( 512, 384 ) // doesn't have to match the window
SetOrientationAllowed( 1, 1, 1, 1 ) // allow both portrait and landscape on mobile devices
SetSyncRate( 30, 0 ) // 30fps instead of 60 to save battery
SetScissor( 0,0,0,0 ) // use the maximum available screen space, no black borders
UseNewDefaultFonts( 1 )
global patch$ as string = ""
SetPatchString()
ReadButton = 1
AddVirtualButton( ReadButton, 64, 32, 64 )
SetVirtualButtonText( ReadButton, "Read" )
global feedback as integer = 0
global working as integer = 0
SetFeedback( "This utility will patch AGKPlayer.js"+chr(10)+" shared variable functions" )
type tPatchFile
olddata$ as string
newdata$ as string
iframecookieoption as integer
endtype
file as tPatchFile
pollreadtext as integer = 0
if debug then FileToStringVariable( "patch.txt", "patchvariable.txt" )
if CompareString( "html5", GetDeviceBaseName())
LoadSharedVariable( "_init", "" )
endif
do
if GetVirtualButtonReleased( ReadButton )
OpenFileDialog( ".js" )
pollreadtext = 1
ResetTimer()
working = 0
endif
if pollreadtext
ResetTimer()
working = 0
SetFeedback( "Waiting for file." )
if GetTextFile( file ) // get text, if text is ready then patch it for download and stop polling
pollreadtext = 0
SetFeedback( "Working..." )
working = 1
if PatchAGKPlayer( file ) // if patch was successful then download is ready
DownLoadText( "AGKPlayer.js", file.newdata$ )
endif
endif
endif
sync()
loop
function SetFeedback( text$ as string )
if not feedback
feedback = CreateText( "" )
SetTextSize( feedback, 25 )
SetTextPosition( feedback, 100, 100 )
endif
if working
SetTextString( feedback, text$ + ", " + str(Timer(),2) + "s" )
else
SetTextString( feedback, text$ )
endif
sync()
endfunction
function OpenFileDialog( filetype as string )
if CompareString( "html5", GetDeviceBaseName())
SaveSharedVariable( "_fr", ".js" )
endif
endfunction
function ReadEntireFile( fd as integer )
text$ as string = ""
if not FileEOF( fd ) then text$ = ReadLine( fd )
while not FileEOF( fd )
text$ = text$ + chr(13) + ReadLine( fd )
endwhile
endfunction text$
function GetTextFile( file ref as tPatchFile )
file.olddata$ = ""
file.newdata$ = ""
if CompareString( "html5", GetDeviceBaseName())
file.olddata$ = LoadSharedVariable( "_fr", "" )
exitfunction len( file.olddata$ )
else
fd = OpenToRead( "AGKPlayer.js" )
file.olddata$ = ReadEntireFile( fd )
CloseFile( fd )
exitfunction 1
endif
endfunction 0
// find the position of the matching find$ {} within text$
// text$[pos] presumed to be find$[1]
function StringFindClosing( text$ as string, pos as integer, find$ as string )
depth as integer = 1
while depth and pos < len( text$ )
inc pos
c$ = mid( text$, pos, 1 )
select FindString( find$, c$, 1, 1 )
case 1: inc depth : endcase
case 2: dec depth : endcase
endselect
if not Random(0,100) then SetFeedback( "Working... " + str(pos) + " of " + str(len(text$)))
endwhile
if not depth then exitfunction pos
endfunction 0
function FindStringD( text$ as string, find$ as string, ignorecase as integer, pos as integer )
while pos <= len( text$ )
if CompareString( find$, mid( text$, pos, len(find$))) then exitfunction pos
inc pos
if not Random(0,100) then SetFeedback( "Working... " + str(pos) + " of " + str(len(text$)))
endwhile
endfunction 0
function PatchAGKPlayer( file ref as tPatchFile )
pos as integer = 30000
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = FindString( file.olddata$, "alert(Pointer_stringify($0))", 1, pos )
if not pos
SetFeedback( "Patch failed, couldn't start." ) : exitfunction 0
endif
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = FindString( file.olddata$, "{", 1, pos )
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = FindString( file.olddata$, "{", 1, pos )
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
if debug
fd = OpenToWrite( "Partone.txt" )
WriteLine( fd, "Writing " + str(pos) + " characters." )
WriteLine( fd, left( file.olddata$, pos ))
CloseFile( fd )
endif
partoneend as integer
partoneend = pos
pos = StringFindClosing( file.olddata$, pos, "{}" )
if not pos
SetFeedback( "Patch failed, couldn't isolate functions." ) : exitfunction 0
endif
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = FindString( file.olddata$, "{", 1, pos )
pos = FindString( file.olddata$, "{", 1, pos )
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = StringFindClosing( file.olddata$, pos, "{}" )
if not pos
SetFeedback( "Patch failed, couldn't isolate functions." ) : exitfunction 0
endif
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = FindString( file.olddata$, "{", 1, pos )
pos = FindString( file.olddata$, "{", 1, pos )
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
pos = StringFindClosing( file.olddata$, pos, "{}" )
if not pos
SetFeedback( "Patch failed, couldn't isolate functions." ) : exitfunction 0
endif
SetFeedback( "Working... " + str(pos) + " of " + str(len(file.olddata$)))
if debug
fd = OpenToWrite( "Parttwo.txt" )
WriteLine( fd, "Writing " + str(pos-partoneend) + " characters." )
WriteLine( fd, mid( file.olddata$, partoneend+1, pos-partoneend ))
CloseFile( fd )
fd = OpenToWrite( "Partthree.txt" )
WriteLine( fd, "Writing " + str(len(file.olddata$)-pos) + " characters." )
WriteLine( fd, right( file.olddata$, 1+len(file.olddata$)-pos))
CloseFile( fd )
endif
file.newdata$ = left( file.olddata$, partoneend ) + patch$ + right( file.olddata$, 1+len(file.olddata$)-pos)
SetFeedback( "Done" )
endfunction 1
function DownloadText( filename$ as string, data$ as string )
if CompareString( "html5", GetDeviceBaseName())
SaveSharedVariable( "_fd" + filename$, data$ )
else
fd = OpenToWrite( filename$ )
WriteLine( fd, data$ )
CloseFile( fd )
endif
endfunction
function FileToStringVariable( filename$ as string, outfilename$ as string )
fdin = OpenToRead( filename$ )
fdout = OpenToWrite( outfilename$ )
WriteLine( fdout, "patch$ = " + chr(0x22) + chr(0x22))
while not FileEOF( fdin )
WriteLine( fdout, "patch$ = patch$ + " + chr(0x22) + ReadLine( fdin ) + chr(0x22) + "+chr(13)")
endwhile
CloseFile( fdout )
CloseFile( fdin )
endfunction
function SetPatchString()
patch$ = ""
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var cookieName = UTF8ToString($0);"+chr(13)
patch$ = patch$ + " var cookieValue = UTF8ToString($1);"+chr(13)
patch$ = patch$ + " var d = new Date;"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (cookieName === '_clipboard')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var elem = window.document.createElement( 'a' )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.addEventListener( 'click', (event) =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var textArea = document.createElement('textarea');"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " textArea.value = cookieValue;"+chr(13)
patch$ = patch$ + " textArea.style.top = '0';"+chr(13)
patch$ = patch$ + " textArea.style.left = '0';"+chr(13)
patch$ = patch$ + " textArea.style.position = 'fixed';"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " document.body.appendChild(textArea);"+chr(13)
patch$ = patch$ + " textArea.focus();"+chr(13)
patch$ = patch$ + " textArea.select();"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " try { document.execCommand('copy'); } catch (err) {}"+chr(13)
patch$ = patch$ + " document.body.removeChild( textArea )"+chr(13)
patch$ = patch$ + " });"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName === '_fullscreen')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var c = Module['canvas']"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (c.requestFullscreen)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " c.requestFullscreen();"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (c.webkitRequestFullscreen)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " c.webkitRequestFullscreen();"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (c.msRequestFullscreen)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " c.msRequestFullscreen();"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_frb' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var elem = window.document.getElementById( 'file-input' )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (!elem)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem = window.document.createElement( 'input' );"+chr(13)
patch$ = patch$ + " elem.type = 'file';"+chr(13)
patch$ = patch$ + " elem.id = 'file-input';"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.addEventListener( 'change', (event) =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const [file] = event.target.files;"+chr(13)
patch$ = patch$ + " const reader = new FileReader();"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " reader.addEventListener( 'load', () => { readerresult = reader.result }, false );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (file)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try {"+chr(13)
patch$ = patch$ + " reader.readAsArrayBuffer( file );"+chr(13)
patch$ = patch$ + " storage['_ssbinaryfilename'] = file.name ? file.name : '' ; } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }, false );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " if (cookieValue.indexOf( '.' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem.accept = cookieValue;"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem.accept = '';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " try { storage['_ssbinaryfilename'] = ''; } catch(e) {}"+chr(13)
patch$ = patch$ + " readerresult = 0"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_fr' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var elem = window.document.getElementById( 'file-input' )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (!elem)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem = window.document.createElement( 'input' );"+chr(13)
patch$ = patch$ + " elem.type = 'file';"+chr(13)
patch$ = patch$ + " elem.id = 'file-input';"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.addEventListener( 'change', (event) =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const [file] = event.target.files;"+chr(13)
patch$ = patch$ + " const reader = new FileReader();"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " reader.addEventListener( 'load', () => { readerresult = reader.result }, false );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (file)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try {"+chr(13)
patch$ = patch$ + " reader.readAsText( file );"+chr(13)
patch$ = patch$ + " storage['_sstextfilename'] = file.name ? file.name : ''; } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }, false );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " if (cookieValue.indexOf( '.' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem.accept = cookieValue;"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " elem.accept = '.txt';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " try { storage['_sstextfilename'] = ''; } catch(e) {}"+chr(13)
patch$ = patch$ + " readerresult = 0"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_fj' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " data = JSON.parse( cookieValue );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (data.datatype === data.conversiontype)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var bytes = new Uint8ClampedArray( data.bytes.length )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " for (var i = 0; i < bytes.length; i++) { bytes[i] = data.bytes[i] }"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " const blob = new Blob([bytes], {type: data.datatype})"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (window.navigator.msSaveOrOpenBlob)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " window.navigator.msSaveBlob( blob, data.filename );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const elem = window.document.createElement('a');"+chr(13)
patch$ = patch$ + " const url = window.URL.createObjectURL( blob );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.href = url"+chr(13)
patch$ = patch$ + " elem.download = data.filename;"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " window.URL.revokeObjectURL(url);"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const mime = data.conversiontype"+chr(13)
patch$ = patch$ + " var canvas = document.createElement('canvas')"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " canvas.width = data.width"+chr(13)
patch$ = patch$ + " canvas.height = data.height"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (data.datatype === 'imagedata')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " if (data.bytes.length > 12)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var bytes = new Uint8ClampedArray( data.bytes.length-12 )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " for (var i = 0; i < bytes.length; i++) { bytes[i] = data.bytes[i+12] }"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " const imagedata = new ImageData( bytes, data.width, data.height )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " canvas.getContext( '2d' ).putImageData( imagedata, 0, 0 )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " const elem = window.document.createElement('a');"+chr(13)
patch$ = patch$ + " const url = canvas.toDataURL( mime ).replace( mime, 'image/octet-stream')"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.href = url"+chr(13)
patch$ = patch$ + " elem.download = data.filename;"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " window.URL.revokeObjectURL( url );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var bytes = new Uint8ClampedArray( data.bytes.length )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " for (var i = 0; i < bytes.length; i++) { bytes[i] = data.bytes[i] }"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " const blob = new Blob([bytes], {type: data.datatype})"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " createImageBitmap( blob ).then( imb =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " canvas.getContext( '2d' ).drawImage( imb, 0, 0 )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " const elem = window.document.createElement('a');"+chr(13)
patch$ = patch$ + " const url = canvas.toDataURL( mime ).replace( mime, 'image/octet-stream')"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.href = url"+chr(13)
patch$ = patch$ + " elem.download = data.filename;"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " window.URL.revokeObjectURL( url );"+chr(13)
patch$ = patch$ + " })"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_fd' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const blob = new Blob([cookieValue], {type: 'text/csv'});"+chr(13)
patch$ = patch$ + " var filename = cookieName.substring(3);"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (filename.indexOf( '.' ) < 0) filename = filename + '.txt';"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (window.navigator.msSaveOrOpenBlob)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " window.navigator.msSaveBlob( blob, filename );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const elem = window.document.createElement('a');"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.href = window.URL.createObjectURL( blob );"+chr(13)
patch$ = patch$ + " elem.download = filename;"+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_ls' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " localStorage.setItem( cookieName, cookieValue );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_ss' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try { storage[cookieName] = cookieValue; } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " d.setTime(d.getTime() + 5 * 365 * 24 * 60 * 60 * 1e3);"+chr(13)
patch$ = patch$ + " var expires = 'expires=' + d.toUTCString();"+chr(13)
patch$ = patch$ + " document.cookie = cookieName + '=' + cookieValue + ';' + expires + ';path=/ SameSite=None; Secure';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + "}), (function($0, $1) {"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var cookieName = UTF8ToString($0);"+chr(13)
patch$ = patch$ + " var returnValue = UTF8ToString($1);"+chr(13)
patch$ = patch$ + " var name = cookieName + '=';"+chr(13)
patch$ = patch$ + " var ca = document.cookie.split(';');"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " for (var i = 0; i < ca.length; i++) {"+chr(13)
patch$ = patch$ + " var c = ca[i];"+chr(13)
patch$ = patch$ + " while (c.charAt(0) == ' ') {"+chr(13)
patch$ = patch$ + " c = c.substring(1)"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " if (c.indexOf(name) == 0) {"+chr(13)
patch$ = patch$ + " returnValue = c.substring(name.length, c.length);"+chr(13)
patch$ = patch$ + " break"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (cookieName === '_init')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " kbkeydown = '';"+chr(13)
patch$ = patch$ + " window.addEventListener( 'keydown', (event) =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " kbkeydown = event.code;"+chr(13)
patch$ = patch$ + " }, false );"+chr(13)
patch$ = patch$ + " } catch (e) {}"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " try"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " storage = {};"+chr(13)
patch$ = patch$ + " window.localStorage.getItem( 'test' )"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " catch (e)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = 'This app does not have permission to access Web Storage';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName === '_kbkeydown')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = kbkeydown;"+chr(13)
patch$ = patch$ + " kbkeydown = '';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName === '_clipboard')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var elem = window.document.createElement( 'a' )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " elem.addEventListener( 'click', (event) =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var textArea = document.createElement('textarea');"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " textArea.value = 'This app does not have permission to read the clipboard';"+chr(13)
patch$ = patch$ + " textArea.style.top = '0';"+chr(13)
patch$ = patch$ + " textArea.style.left = '0';"+chr(13)
patch$ = patch$ + " textArea.style.position = 'fixed';"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " document.body.appendChild(textArea);"+chr(13)
patch$ = patch$ + " textArea.focus();"+chr(13)
patch$ = patch$ + " textArea.select();"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " try { document.execCommand('paste'); } catch (err) {}"+chr(13)
patch$ = patch$ + " try { storage['_ssclipboard'] = textArea.value } catch(e) {}"+chr(13)
patch$ = patch$ + " document.body.removeChild( textArea )"+chr(13)
patch$ = patch$ + " });"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " try { storage['_ssclipboard'] = '' } catch(e) {}"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " document.body.appendChild( elem );"+chr(13)
patch$ = patch$ + " elem.click();"+chr(13)
patch$ = patch$ + " document.body.removeChild( elem );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName === 'args')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = window.location.href"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_fr' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " if (cookieName === '_frb')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " if (readerresult)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var bytes = new Uint8ClampedArray( readerresult )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " returnValue = '[' + bytes.join(',') + ']'"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = readerresult"+chr(13)
patch$ = patch$ + " if (!returnValue) returnValue = UTF8ToString($1)"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_fj' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " if (returnValue === '')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " if (data && data.width)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = '[' + data.width + ',' + data.bytes.join(',') + ']';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (readerresult)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var bytes = new Uint8ClampedArray( readerresult )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " data = JSON.parse( returnValue );"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (data.datatype === data.conversiontype || data.datatype === 'imagedata')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = '[' + bytes.join(',') + ']';"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (data.conversiontype === 'imagedata')"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " const blob = new Blob([bytes], {type: data.datatype})"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " createImageBitmap( blob ).then( imb =>"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var canvas = document.createElement('canvas')"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " canvas.width = imb.width"+chr(13)
patch$ = patch$ + " canvas.height = imb.height"+chr(13)
patch$ = patch$ + " canvas.getContext( '2d' ).drawImage( imb, 0, 0 )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " var imagedata = canvas.getContext( '2d' ).getImageData( 0, 0, imb.width, imb.height )"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " data.width = imb.width"+chr(13)
patch$ = patch$ + " data.bytes = new Uint8ClampedArray( imagedata.data )"+chr(13)
patch$ = patch$ + " })"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_ls' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " returnValue = localStorage.getItem( cookieName )"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_ss' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try { returnValue = storage[cookieName] } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (!returnValue) returnValue = UTF8ToString($1)"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " var lengthBytes = lengthBytesUTF8( returnValue ) + 1;"+chr(13)
patch$ = patch$ + " var heapString = _malloc( lengthBytes );"+chr(13)
patch$ = patch$ + " stringToUTF8( returnValue, heapString, lengthBytes );"+chr(13)
patch$ = patch$ + " return heapString"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + "}), (function($0) {"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " var cookieName = UTF8ToString($0);"+chr(13)
patch$ = patch$ + ""+chr(13)
patch$ = patch$ + " if (cookieName.indexOf( '_ls' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " localStorage.removeItem( cookieName );"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_ss' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try { storage[cookieName] = ''; } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_f' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " storage['_sstextfilename'] = '';"+chr(13)
patch$ = patch$ + " storage['_ssbinaryfilename'] = '';"+chr(13)
patch$ = patch$ + " readerresult = 0;"+chr(13)
patch$ = patch$ + " data = 0;"+chr(13)
patch$ = patch$ + " } catch (e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else if (cookieName.indexOf( '_clipboard' ) == 0)"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " try { storage['_ssclipboard'] = ''; } catch(e) {}"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " else"+chr(13)
patch$ = patch$ + " {"+chr(13)
patch$ = patch$ + " document.cookie = cookieName + '=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/'"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + " }"+chr(13)
patch$ = patch$ + ""+chr(13)
endfunction
/**/
Examples in SV.agc include reading and writing images in .png, .jpg, and a raw format as produced by CreateMemblockFromImage(). Reading from the clipboard requires permission, and I'm no javascript expert but my reading is that there's no way to request permission and if your app runs in an iframe that the iframe must explicitly grant your app permission to paste from the clipboard. I've included a clipboard paste function that will probably work if your app has permission, but as with all of this there are no guarantees.
As noted in the demo source, getNoiseImage() and the functions it calls are from
https://forum.thegamecreators.com/thread/228529#msg2670935. Thanks to VirtualNomad and Mike/Orvillian for providing this example and allowing it to be included here.
Edited August 7, 2022 to include fullscreen and detect last key functions
SaveSharedVariable( "_fullscreen", "" ) will make your html5 app go fullscreen, even with a studio build
LoadSharedVariable( "_kbkeydown", "" ) will return a string version of the last key pressed if a key was pressed since the last time this function was called so there's a way to distinguish between numpad and arrow keys
Update October 8, 2022
Trying to open a file picker while in full screen mode causes problems.
The size of an image that can be read or written is quite limited, so exercise caution.