{deck}
version:1
card:0
size:[512,342]
name:"The Forbidden Library"
author:"John Earnest"

{fonts}
olympiad_italic:"%%[encoded data, 5256 characters]"

{card:Index}
image:"%%[encoded data, 29196 characters]"
{widgets}
field1:{"type":"field","size":[354,65],"pos":[79,20],"locked":1,"border":1,"align":"center","value":{"text":["","The Forbidden Library:","\n(Dangerous Extensions Which Should Not Be)\n---\nA series of modules which use the Web-Decker ","danger zone"," to extend Decker's capabilities in non-portable ways, with rationale for their exclusion."],"font":["","olympiad_italic","","",""],"arg":["","","","http://beyondloom.com/decker/decker.html#thedangerzone",""]}}
field2:{"type":"field","size":[27,14],"pos":[7,323],"locked":1,"show":"invert","border":0,"value":"v1.0"}
button1:{"type":"button","size":[60,20],"pos":[170,121],"script":"Index.0","text":"ls","style":"rect"}
button2:{"type":"button","size":[60,20],"pos":[283,175],"script":"Index.0","text":"ajax","style":"rect"}
button3:{"type":"button","size":[60,20],"pos":[160,234],"script":"Index.0","text":"kb","style":"rect"}
button4:{"type":"button","size":[60,20],"pos":[251,306],"script":"Index.0","text":"bgm","style":"rect"}
button5:{"type":"button","size":[131,35],"pos":[370,298],"script":"Index.1","font":"olympiad_italic","show":"invert","text":"A Warning."}

{script:Index.0}
on click do
  go["about-%s" format me.text "BoxIn"]
end
{end}

{script:Index.1}
on click do
  go[warning "BoxIn"]
end
{end}

{card:warning}
image:"%%IMG2AgABVgz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8M/wz/DP8Mrg=="
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"A Warning"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"warning.0","text":"Back","style":"rect"}
field1:{"type":"field","size":[246,245],"pos":[140,52],"locked":1,"border":0,"value":"This is not a place of honor.\n\nThe use of any modules from The Forbidden Library will  prevent your decks from working in Native-Decker: they rely on injecting JavaScript into a web browser and accessing both web APIs and the internal workings of Web-Decker.\n\nIn many cases, the extensions or special behavior enabled by these libraries transcends the normal life-cycle of Decks, and as a result can persist when reloading the current deck or opening another deck.\n\nThese libraries are provided as practical examples of how to use danger.js[], and to address several common feature requests which- for reasons enumerated within this document- I do not think are appropriate for mainline Decker.\n\n-John Earnest / Internet Janitor"}

{script:warning.0}
on click do
  go["Back" "BoxOut"]
end
{end}

{card:about-ls}
image:"%%IMG2AgABVgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wCeAcwA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AQEAygEBAP8ANQEBAMoBAQD/ADUBAQDKAQEA/wA1AcwA/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wCA"
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"ls: Browser Local Storage"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"warning.0","text":"Back","style":"rect"}
button2:{"type":"button","size":[96,20],"pos":[409,316],"script":"about-ls.0","text":"See Inside...","style":"rect"}
persisted:{"type":"field","size":[163,20],"pos":[46,121],"volatile":1}
button3:{"type":"button","size":[72,23],"pos":[46,86],"script":"about-ls.1","text":"ls set"}
button4:{"type":"button","size":[72,23],"pos":[137,86],"script":"about-ls.2","text":"ls get"}
field1:{"type":"field","size":[256,122],"pos":[249,47],"locked":1,"border":0,"value":"the ls.set[key value] function attempts to store a string \"value\" in local storage, associated with a string \"key\".\n\nthe ls.get[key] function attempts to retrieve a string from local storage associated with the string \"key\". If no entry is found, return 0."}
field2:{"type":"field","size":[256,134],"pos":[249,176],"locked":1,"show":"invert","value":{"text":["","Why Not?","\n\nDecker does not ordinarily expose LocalStorage because it violates the principle of keeping a deck's state within the deck.\n\nStashing important information in LocalStorage can be brittle and error prone, and it would not be available to native decker or even a local copy of web-decker saved from the web."],"font":["","olympiad_italic",""],"arg":["","",""]}}

{script:about-ls.0}
on click do
 modviewer.event["init" (6 drop card.name)]
 go[modviewer "SlideUp"]
end
{end}

{script:about-ls.1}
on click do
 ls.set["local-storage-key" persisted.text]
end
{end}

{script:about-ls.2}
on click do
 persisted.text:ls.get["local-storage-key"]
end
{end}

{card:about-ajax}
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"ajax: Asynchronous HTTP Requests"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"warning.0","text":"Back","style":"rect"}
button2:{"type":"button","size":[96,20],"pos":[409,316],"script":"about-ls.0","text":"See Inside...","style":"rect"}
field1:{"type":"field","size":[256,96],"pos":[249,47],"locked":1,"border":0,"value":{"text":["the ajax.send[verb url payload target] function makes an HTTP request with verb (usually GET or POST) to a url  with a string payload. If a response is recieved, the target will be sent a \"http_response[x]\" event with the response value as a string.\n\nthere are some interesting web APIs which do not require authentication listed ","here",", but not all of them appear to provide wildcard CORS headers."],"font":["","",""],"arg":["","https://mixedanalytics.com/blog/list-actually-free-open-no-auth-needed-apis/",""]}}
field2:{"type":"field","size":[256,157],"pos":[249,153],"locked":1,"show":"invert","value":{"text":["","Why Not?","\n\nDecker does not ordinarily support making HTTP requests out of concern for fidelity and security.\n\nMaking HTTP requests is complex, and would likely draw in a dependency on libcurl in Native-Decker. Web-Decker is subject to ","CORS"," restrictions, which would severely limit consistency between Web- and Native-Decker, and in many cases also render locally-saved instances of Web-Decker nonfunctional. Making remote requests can be used maliciously, violating user privacy, and depending upon remote resources can cause decks to break in the future if those services become unavailable."],"font":["","olympiad_italic","","",""],"arg":["","","","https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS",""]}}
response:{"type":"field","size":[236,196],"pos":[6,114],"locked":1,"volatile":1,"scrollbar":1,"style":"code"}
button3:{"type":"button","size":[47,25],"pos":[186,64],"script":"about-ajax.0","text":"GET"}
url:{"type":"field","size":[163,25],"pos":[15,64],"style":"code","value":"http://httpbin.org/get"}

{script:about-ajax.0}
on click do
 ajax.send["GET" url.text 0 me]
 response.text:"<awaiting response...>"
end

on http_response x do
 response.text:x
end
{end}

{card:about-kb}
script:"about-kb.0"
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"kb: Raw Keyboard Events"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"warning.0","text":"Back","style":"rect"}
button2:{"type":"button","size":[96,20],"pos":[409,316],"script":"about-ls.0","text":"See Inside...","style":"rect"}
field1:{"type":"field","size":[256,96],"pos":[249,47],"locked":1,"border":0,"value":"when installed, the kb module sends key_down[key] and key_up[key] events to the active card with the string name of each key that is pressed or released (respectively) while the user is in Interact mode, with no field or grid selected, and no modal dialogs open."}
field2:{"type":"field","size":[256,157],"pos":[249,153],"locked":1,"show":"invert","value":{"text":["","Why Not?","\n\nDecker does not ordinarily expose raw key up/key down events because Decker is designed to work on touch-based devices which don't necessarily have a physical keyboard. Exposing such events would enable the creation of decks which do not work on such devices.\n\nThe navigate[] event and button shortcuts are available as a more portable alternative that can still suit many applications."],"font":["","olympiad_italic",""],"arg":["","",""]}}
log:{"type":"field","size":[236,241],"pos":[6,69],"locked":1,"volatile":1,"scrollbar":1,"style":"code"}
field3:{"type":"field","size":[236,14],"pos":[6,51],"locked":1,"border":0,"align":"center","value":"keyboard event log"}

{script:about-kb.0}
on enlog x do
 log.text:log.text,"\n",x
 log.scroll:1000000
end

on key_up key do
 enlog["up: <%s>" format key]
end

on key_down key do
 enlog["down: <%s>" format key]
end
{end}

{card:about-bgm}
image:"%%IMG2AgABVgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8AfwHLAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgEBAMkBAQD/ADYBAQDJAQEA/wA2AQEAyQEBAP8ANgHLAP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wBS"
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"bgm: Background Music from External Files"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"warning.0","text":"Back","style":"rect"}
button2:{"type":"button","size":[96,20],"pos":[409,316],"script":"about-ls.0","text":"See Inside...","style":"rect"}
field1:{"type":"field","size":[256,108],"pos":[249,47],"locked":1,"border":0,"value":"the bgm.play[path] function attempts to load an audio file (.mp3 or .wav are supported by most browsers) at a path relative to the current page and begin playing it.\n\nthe bgm.stop[] function stops any music that is currently playing.\n\nthe bgm.volume[x] function accepts a number x ranging from 0 to 1 which controls the relative volume of the currently playing music."}
field2:{"type":"field","size":[256,146],"pos":[249,164],"locked":1,"show":"invert","value":{"text":["","Why Not?","\n\nDecker does not ordinarily support audio from external files because it violates the principle of keeping a deck's resources within the deck.\n\nAudio files tend to be very large relative to decks, and can quickly bloat their size. Supporting modern audio codecs would add large dependencies to Native-Decker. Decker restricts audio to short, low-quality samples to keep them small enough to embed directly in decks and easy to decode."],"font":["","olympiad_italic",""],"arg":["","",""]}}
button4:{"type":"button","size":[84,20],"pos":[35,106],"script":"about-bgm.0","text":"Play Music"}
button5:{"type":"button","size":[85,20],"pos":[130,106],"script":"about-bgm.1","text":"Stop Music"}
volume:{"type":"slider","size":[180,25],"pos":[35,149],"script":"about-bgm.2","interval":[0,1],"value":0.8,"step":0.05}
field3:{"type":"field","size":[180,14],"pos":[35,133],"locked":1,"border":0,"align":"center","value":"music volume:"}
song:{"type":"field","size":[180,20],"pos":[35,78],"value":"../llama/sounds/lullaby.mp3"}
field4:{"type":"field","size":[180,14],"pos":[35,63],"locked":1,"border":0,"align":"center","value":"file path:"}
field5:{"type":"field","size":[180,55],"pos":[35,202],"locked":1,"border":0,"align":"center","value":"(Note that if you've saved this deck locally or are using it anywhere but the official published location, you probably don't have the above music file.\nStarting to see the problem here?)"}

{script:about-bgm.0}
on click do
 bgm.play[song.text]
 bgm.volume[volume.value]
end
{end}

{script:about-bgm.1}
on click do
 bgm.stop[]
end
{end}

{script:about-bgm.2}
on change val do
 bgm.volume[val]
end
{end}

{card:modviewer}
script:"modviewer.0"
{widgets}
title:{"type":"field","size":[499,17],"pos":[6,21],"locked":1,"font":"menu","show":"invert","align":"center","value":"Module Script: bgm"}
button1:{"type":"button","size":[60,20],"pos":[6,316],"script":"modviewer.1","text":"Back","style":"rect"}
code:{"type":"field","size":[499,267],"pos":[6,43],"volatile":1,"scrollbar":1,"style":"code"}

{script:modviewer.0}
on init name do
 title.text:"Module Script: %s" format name
 code.text:deck.modules[name].script
end
{end}

{script:modviewer.1}
on click do
  go["Back" "SlideDown"]
end
{end}

{module:ajax}
description:"A Dangerous JS binding for making asynchronous HTTP requests."
version:1
{script}
on validate do
 if !sys.platform~"web" alert["the 'ajax' module only works in web-decker."]
 elseif !danger.js alert["the 'ajax' module requires the danger zone."]
 end
end

# for some applications it may be desirable to extend this to handle
# binary response data/payloads or to inform the caller if we get
# a response code other than "OK":

natives:danger.js["_=>({l}
 send:function send(verb,url,payload,callback){l}
  const x=new XMLHttpRequest()
  x.onreadystatechange=_=>{l}
   if(x.readyState==XMLHttpRequest.DONE&&x.status==200)callback(x.responseText)
  {r}
  x.open(verb,url)
  x.send(payload)
 {r},
{r})" 0]

module.send:on _ verb url payload target do
 validate[]
 on callback x do target.event["http_response" x] end
 natives.send[verb url payload callback]
end
{end}

{module:ls}
description:"A Dangerous JS binding for interacting with browser local storage."
version:1
{script}
on validate do
 if !sys.platform~"web" alert["the 'ls' module only works in web-decker."]
 elseif !danger.js alert["the 'ls' module requires the danger zone."]
 end
end

# in some browsers/contexts interacting with localstorage may not be
# allowed at all, so we need to guard against internal explosions:

natives:danger.js["_=>({l}
 set:function set(k,v){l}
  try{l}localStorage.setItem(k,v){r}catch(e){l}{r};return v
 {r},
 get:function get(k,v){l}
  try{l}return localStorage.getItem(k){r}catch(e){l}{r};return null
 {r},
{r})" 0]

module.set:on set key value do validate[] natives.set[key value] end
module.get:on get key       do validate[] natives.get[key      ] end
{end}

{module:kb}
description:"A Dangerous JS binding for obtaining raw keyboard events."
version:1
{script}
callbacks.down:on key_down deck key do deck.card.event["key_down" key] end
callbacks.up  :on key_up   deck key do deck.card.event["key_up"   key] end

# wrap the low-level input handlers for Decker itself and
# redirect a copy of these events to the active card if
# nothing else ought to have focus and we're in interact mode.
# some of this is rather brittle, because it relies on unstable
# internal functions comprising Web-Decker.

# these should only be installed once, or else they'll stack
# up nested handlers every time the module is initialized!

danger.js["callbacks=>{l}
 function nofocus(){l}
  return uimode=='interact'&&ms.type==null&&!wid.fv&&!wid.gv
 {r}
 if(!ext.kb){l}
  const key_down=document.body.onkeydown
  const key_up  =document.body.onkeyup
  document.body.onkeydown=e=>{l}
   key_down(e)
   if(nofocus())callbacks.down(deck,e.key)
  {r}
  document.body.onkeyup=e=>{l}
   key_up(e)
   if(nofocus())callbacks.up(deck,e.key)
  {r}
  ext.kb=1
 {r}
{r}" callbacks]
{end}

{module:bgm}
description:"A Dangerous JS binding for playing looped background music from external media files."
version:1
{script}
on validate do
 if !sys.platform~"web" alert["the 'bgm' module only works in web-decker."]
 elseif !danger.js alert["the 'bgm' module requires the danger zone."]
 end
end

# safari requires audio playback be initiated in direct response to
# user action the first time it happens, so we play a dummy sound
# in response to a user's clicks.

# stash the Audio object in ext.bgm.a so that we can access it (and stop it)
# even if the deck is reloaded:

natives:danger.js["_=>{l}
 if(!ext.bgm){l}
  ext.bgm={l}init:0,a:null{r}
  document.body.addEventListener('mousedown',_=>{l}
   if(!ext.bgm.init){l}
    new Audio().play().catch(e=>console.log('init',e))
    ext.bgm.init=1
   {r}
  {r})
 {r}

 function volume(x){l}if(ext.bgm.a)ext.bgm.a.volume=x{r}
 function stop(){l}if(ext.bgm.a)ext.bgm.a.pause(){r}
 function play(path){l}
  stop()
  ext.bgm.a=new Audio(path)
  ext.bgm.a.loop=true
  ext.bgm.a.play().catch(e=>{l}console.log(e){r})
 {r}

 return {l}play,stop,volume{r}
{r}" 0]

module.play  :on play path do validate[] natives.play[path]    end
module.stop  :on stop      do validate[] natives.stop[]        end
module.volume:on volume x  do validate[] natives.volume[0|1&x] end
{end}

