diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f227c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,40 @@ +testing/ +util.sh + +# Created by https://www.toptal.com/developers/gitignore/api/macos +# Edit at https://www.toptal.com/developers/gitignore?templates=macos + +### macOS ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### macOS Patch ### +# iCloud generated files +*.icloud + +# End of https://www.toptal.com/developers/gitignore/api/macos \ No newline at end of file diff --git a/icons/apl-logo.png b/icons/apl-logo.png new file mode 100644 index 0000000..65dac04 Binary files /dev/null and b/icons/apl-logo.png differ diff --git a/index.html b/index.html index 056a802..903f008 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,15 @@ ngn/apl + + + + + + + +

Demo

diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..691f153 --- /dev/null +++ b/manifest.json @@ -0,0 +1,12 @@ +{ + "name": "ngn/apl", + "icons": [ + { + "src": "icons/apl-logo.png", + "type": "image/png", + "sizes": "512x512" + } + ], + "start_url": "https://abrudz.github.io/ngn-apl/web/index.html", + "display": "standalone" +} \ No newline at end of file diff --git a/ngn-apl-screenshot.png b/ngn-apl-screenshot.png new file mode 100644 index 0000000..e877c60 Binary files /dev/null and b/ngn-apl-screenshot.png differ diff --git a/readme.md b/readme.md index 7f4a394..f41c0e7 100644 --- a/readme.md +++ b/readme.md @@ -4,7 +4,9 @@ Its author thinks it has served its purpose and has become a distraction. He wen ---- -**[Demo](https://abrudz.github.io/ngn-apl)**
+[Online Demo](https://abrudz.github.io/ngn-apl) - can also be installed offline as a [PWA (Progressive Web App)](https://developer.mozilla.org/en-US/docs/Web/Progressive_web_apps/Guides/What_is_a_progressive_web_app) + +![Screenshot](ngn-apl-screenshot.png) An [APL](https://aplwiki.com) interpreter written in JavaScript. Runs in a browser or [NodeJS](https://nodejs.org/). @@ -18,7 +20,7 @@ prototypes, modified assignment (`x+←1`), control structures (`:If`), object-o Used in [Paul L Jackson's web site](https://plj541.github.io/APL.js/), [repl.it](https://repl.it/languages/APL), and [tio.run](https://tio.run/#apl-ngn). -# Offline usage +# Offline usage with NodeJS Run `apl.js` with [Node](https://nodejs.org/) to start a REPL: diff --git a/serviceWorker.js b/serviceWorker.js new file mode 100644 index 0000000..6cb1134 --- /dev/null +++ b/serviceWorker.js @@ -0,0 +1,70 @@ +console.log('Service Worker running') +const CACHE_NAME = 'ngn/apl-v1'; +const urlsToCache = [ + '../icons/apl-logo.png', + '../web/index.html', + '../web/Apl385.woff', // font + '../web/index.js', + '../web/lb.js', + '../apl.js', + '../t.js', + '../t.apl' +]; + +async function addToCache() { + try { + let cache = await caches.open(CACHE_NAME); + console.log('Opened cache'); + await cache.addAll(urlsToCache); + console.log('All urls cached') + } catch(error) { + console.error('One or more URLs failed to cache:', error.url || error); + } +} + +self.addEventListener('install', function(event) { + console.log('Install - Add required urls to cache') + event.waitUntil(addToCache()); +}); + +// Function acts as proxy - any network request goes through this function +async function fetchEvent(event) { + let cacheResponse = await caches.match(event.request); + console.log('Cache hit', event.request.url) + if (cacheResponse) { + return cacheResponse; + } + + // Ideally there should be no cache miss + // as all urls should already be added to cache when service worker was installed + console.log('Cache missed', event.request.url) + + // IMPORTANT: Clone the request. A request is a stream and + // can only be consumed once. Since we are consuming this + // once by cache and once by the browser for fetch, we need + // to clone the response. + let fetchRequest = event.request.clone(); + + let response = await fetch(fetchRequest); + // Check if we received a valid response + if(!response || response.status !== 200 || response.type !== 'basic') { + console.log('Invalid response', event.request.url, response); + return response; + } + + // IMPORTANT: Clone the response. A response is a stream + // and because we want the browser to consume the response + // as well as the cache consuming the response, we need + // to clone it so we have two streams. + let responseToCache = response.clone(); + + let cache = await caches.open(CACHE_NAME); + await cache.put(event.request, responseToCache); + console.log('Put in cache', event.request.url); + + return response; +} + +self.addEventListener('fetch', function(event) { + event.respondWith(fetchEvent(event)); +}); diff --git a/web/index.html b/web/index.html index 8775cf2..b83de55 100644 --- a/web/index.html +++ b/web/index.html @@ -1,5 +1,19 @@ -ngn/apl