├── .github
└── welcome-bot.yml
├── CODE_OF_CONDUCT.md
├── LICENSE
├── README.md
├── a2hs
├── icon
│ └── fox-icon.png
├── images
│ ├── fox1.jpg
│ ├── fox2.jpg
│ ├── fox3.jpg
│ └── fox4.jpg
├── index.html
├── index.js
├── manifest.webmanifest
├── style.css
└── sw.js
├── cycletracker
├── html_and_css
│ ├── index.html
│ └── style.css
├── index.html
├── javascript_functionality
│ ├── app.js
│ ├── index.html
│ └── style.css
├── manifest_file
│ ├── app.js
│ ├── cycletracker.json
│ ├── favicon.ico
│ ├── icons
│ │ ├── circle.ico
│ │ ├── circle.svg
│ │ ├── tire.svg
│ │ └── wheel.svg
│ ├── index.html
│ └── style.css
└── service_workers
│ ├── app.js
│ ├── cycletracker.json
│ ├── favicon.ico
│ ├── icons
│ ├── circle.ico
│ ├── circle.svg
│ ├── tire.svg
│ └── wheel.svg
│ ├── index.html
│ ├── style.css
│ └── sw.js
└── js13kpwa
├── .gitignore
├── app.js
├── data
├── games.js
└── img
│ ├── a-box-invaders.jpg
│ ├── a-snake.jpg
│ ├── balloon-problems.jpg
│ ├── cat-meow.jpg
│ ├── coconutty.jpg
│ ├── debriss.jpg
│ ├── dont-let-your-dreams-be-memes.jpg
│ ├── emma-3d.jpg
│ ├── fly-south.jpg
│ ├── galacticdiamond.jpg
│ ├── give-space.jpg
│ ├── lost-in-cyberspace.jpg
│ ├── lost-in-guam.jpg
│ ├── lost-in-metaverse.jpg
│ ├── lost-in-my-mind.jpg
│ ├── lost-in-the-forest-dungeon.jpg
│ ├── lost-pacman.jpg
│ ├── metamorphosis.jpg
│ ├── placeholder.png
│ ├── polyhedron-runner.jpg
│ ├── prisonri0t.jpg
│ ├── she-is-my-universe.jpg
│ ├── shifted-dimensions.jpg
│ ├── spacewrecked.jpg
│ ├── vernissage.jpg
│ ├── vr-racing.jpg
│ ├── wandering-moon.jpg
│ ├── wherewhat.jpg
│ └── world-lost.jpg
├── favicon.ico
├── fonts
├── graduate.eot
├── graduate.ttf
└── graduate.woff
├── icons
├── icon-128.png
├── icon-168.png
├── icon-192.png
├── icon-256.png
├── icon-32.png
├── icon-512.png
├── icon-64.png
└── icon-96.png
├── img
├── bg.png
└── js13kgames.png
├── index.html
├── js13kpwa.webmanifest
├── style.css
└── sw.js
/.github/welcome-bot.yml:
--------------------------------------------------------------------------------
1 |
2 | name: "AlloAllo"
3 |
4 | on:
5 | issues:
6 | types:
7 | - opened
8 | pull_request_target:
9 | branches:
10 | - main
11 | types:
12 | - opened
13 | - closed
14 |
15 | jobs:
16 | allo-allo:
17 | uses: mdn/workflows/.github/workflows/allo-allo.yml@main
18 | with:
19 | target-repo: "mdn/pwa-examples"
20 | issue-welcome: >
21 | It looks like this is your first issue. Welcome! 👋
22 | One of the project maintainers will be with you as soon as possible. We
23 | appreciate your patience. To safeguard the health of the project, please
24 | take a moment to read our [code of conduct](../blob/main/CODE_OF_CONDUCT.md).
25 | pr-welcome: >
26 | It looks like this is your first pull request. 🎉
27 | Thank you for your contribution! One of the project maintainers will triage
28 | and assign the pull request for review. We appreciate your patience. To
29 | safeguard the health of the project, please take a moment to read our
30 | [code of conduct](../blob/main/CODE_OF_CONDUCT.md).
31 | pr-merged: >
32 | Congratulations on your first merged pull request. 🎉 Thank you for your contribution!
33 | Did you know we have a [project board](https://github.com/orgs/mdn/projects/25) with high-impact contribution opportunities?
34 | We look forward to your next contribution.
35 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Community Participation Guidelines
2 |
3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines.
4 | For more details, please read the
5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/).
6 |
7 | ## How to Report
8 | For more information on how to report violations of the Community Participation Guidelines, please read our [How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/) page.
9 |
10 |
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | CC0 1.0 Universal
2 |
3 | Statement of Purpose
4 |
5 | The laws of most jurisdictions throughout the world automatically confer
6 | exclusive Copyright and Related Rights (defined below) upon the creator and
7 | subsequent owner(s) (each and all, an "owner") of an original work of
8 | authorship and/or a database (each, a "Work").
9 |
10 | Certain owners wish to permanently relinquish those rights to a Work for the
11 | purpose of contributing to a commons of creative, cultural and scientific
12 | works ("Commons") that the public can reliably and without fear of later
13 | claims of infringement build upon, modify, incorporate in other works, reuse
14 | and redistribute as freely as possible in any form whatsoever and for any
15 | purposes, including without limitation commercial purposes. These owners may
16 | contribute to the Commons to promote the ideal of a free culture and the
17 | further production of creative, cultural and scientific works, or to gain
18 | reputation or greater distribution for their Work in part through the use and
19 | efforts of others.
20 |
21 | For these and/or other purposes and motivations, and without any expectation
22 | of additional consideration or compensation, the person associating CC0 with a
23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
25 | and publicly distribute the Work under its terms, with knowledge of his or her
26 | Copyright and Related Rights in the Work and the meaning and intended legal
27 | effect of CC0 on those rights.
28 |
29 | 1. Copyright and Related Rights. A Work made available under CC0 may be
30 | protected by copyright and related or neighboring rights ("Copyright and
31 | Related Rights"). Copyright and Related Rights include, but are not limited
32 | to, the following:
33 |
34 | i. the right to reproduce, adapt, distribute, perform, display, communicate,
35 | and translate a Work;
36 |
37 | ii. moral rights retained by the original author(s) and/or performer(s);
38 |
39 | iii. publicity and privacy rights pertaining to a person's image or likeness
40 | depicted in a Work;
41 |
42 | iv. rights protecting against unfair competition in regards to a Work,
43 | subject to the limitations in paragraph 4(a), below;
44 |
45 | v. rights protecting the extraction, dissemination, use and reuse of data in
46 | a Work;
47 |
48 | vi. database rights (such as those arising under Directive 96/9/EC of the
49 | European Parliament and of the Council of 11 March 1996 on the legal
50 | protection of databases, and under any national implementation thereof,
51 | including any amended or successor version of such directive); and
52 |
53 | vii. other similar, equivalent or corresponding rights throughout the world
54 | based on applicable law or treaty, and any national implementations thereof.
55 |
56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of,
57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
59 | and Related Rights and associated claims and causes of action, whether now
60 | known or unknown (including existing as well as future claims and causes of
61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum
62 | duration provided by applicable law or treaty (including future time
63 | extensions), (iii) in any current or future medium and for any number of
64 | copies, and (iv) for any purpose whatsoever, including without limitation
65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
66 | the Waiver for the benefit of each member of the public at large and to the
67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver
68 | shall not be subject to revocation, rescission, cancellation, termination, or
69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work
70 | by the public as contemplated by Affirmer's express Statement of Purpose.
71 |
72 | 3. Public License Fallback. Should any part of the Waiver for any reason be
73 | judged legally invalid or ineffective under applicable law, then the Waiver
74 | shall be preserved to the maximum extent permitted taking into account
75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
76 | is so judged Affirmer hereby grants to each affected person a royalty-free,
77 | non transferable, non sublicensable, non exclusive, irrevocable and
78 | unconditional license to exercise Affirmer's Copyright and Related Rights in
79 | the Work (i) in all territories worldwide, (ii) for the maximum duration
80 | provided by applicable law or treaty (including future time extensions), (iii)
81 | in any current or future medium and for any number of copies, and (iv) for any
82 | purpose whatsoever, including without limitation commercial, advertising or
83 | promotional purposes (the "License"). The License shall be deemed effective as
84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the
85 | License for any reason be judged legally invalid or ineffective under
86 | applicable law, such partial invalidity or ineffectiveness shall not
87 | invalidate the remainder of the License, and in such case Affirmer hereby
88 | affirms that he or she will not (i) exercise any of his or her remaining
89 | Copyright and Related Rights in the Work or (ii) assert any associated claims
90 | and causes of action with respect to the Work, in either case contrary to
91 | Affirmer's express Statement of Purpose.
92 |
93 | 4. Limitations and Disclaimers.
94 |
95 | a. No trademark or patent rights held by Affirmer are waived, abandoned,
96 | surrendered, licensed or otherwise affected by this document.
97 |
98 | b. Affirmer offers the Work as-is and makes no representations or warranties
99 | of any kind concerning the Work, express, implied, statutory or otherwise,
100 | including without limitation warranties of title, merchantability, fitness
101 | for a particular purpose, non infringement, or the absence of latent or
102 | other defects, accuracy, or the present or absence of errors, whether or not
103 | discoverable, all to the greatest extent permissible under applicable law.
104 |
105 | c. Affirmer disclaims responsibility for clearing rights of other persons
106 | that may apply to the Work or any use thereof, including without limitation
107 | any person's Copyright and Related Rights in the Work. Further, Affirmer
108 | disclaims responsibility for obtaining any necessary consents, permissions
109 | or other rights required for any use of the Work.
110 |
111 | d. Affirmer understands and acknowledges that Creative Commons is not a
112 | party to this document and has no duty or obligation with respect to this
113 | CC0 or use of the Work.
114 |
115 | For more information, please see
116 |
117 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PWA Examples
2 |
3 | Examples for progressive web apps.
4 |
5 | In this repo, we currently have:
6 |
7 | * [CycleTracker](cycletracker): A basic app for tracking menstrual cycles. The app's HTML includes a form to add a period cycle start and end dates. The JavaScript app functionality sorts the dates and saves thems to local storage. It also displays the dates retrieved from local storage below the form. The app includes a manifest file with three icons, color scheme, etc. The app also includes a service worker to handle asset caching.
8 |
9 | * [a2hs](a2hs): An example set up to show how Add to home screen (A2HS) works. [See it live here](https://mdn.github.io/pwa-examples/a2hs/). This includes an icon and [manifest file](a2hs/manifest.webmanifest) for allowing the app to be added to home screen, and a [simple service worker](a2hs/sw.js) for making the site work offline.
10 |
11 | * [js13kpwa](js13kpwa): A list of A-Frame entries submitted to the js13kGames 2017 competition, used as an example for the MDN articles about Progressive Web Apps. The js13kPWA have the App Shell structure, works offline with the Service Worker, is installable thanks to the Manifest file and Add to Homescreen feature, and is re-engageable by using Notifications and Push. [See it live here](https://mdn.github.io/pwa-examples/js13kpwa/).
12 |
--------------------------------------------------------------------------------
/a2hs/icon/fox-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/a2hs/icon/fox-icon.png
--------------------------------------------------------------------------------
/a2hs/images/fox1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/a2hs/images/fox1.jpg
--------------------------------------------------------------------------------
/a2hs/images/fox2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/a2hs/images/fox2.jpg
--------------------------------------------------------------------------------
/a2hs/images/fox3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/a2hs/images/fox3.jpg
--------------------------------------------------------------------------------
/a2hs/images/fox4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/a2hs/images/fox4.jpg
--------------------------------------------------------------------------------
/a2hs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | A2HS demo
6 |
7 |
8 |
9 |
10 |
11 |
12 | Add to home screen
13 |
14 |
15 |
--------------------------------------------------------------------------------
/a2hs/index.js:
--------------------------------------------------------------------------------
1 | const images = ['fox1', 'fox2', 'fox3', 'fox4'];
2 | const imgElem = document.querySelector('img');
3 |
4 | function randomValueFromArray(array) {
5 | const randomNo = Math.floor(Math.random() * array.length);
6 | return array[randomNo];
7 | }
8 |
9 | setInterval(() => {
10 | const randomChoice = randomValueFromArray(images);
11 | imgElem.src = `images/${randomChoice}.jpg`;
12 | }, 2000);
13 |
14 | // Register service worker to control making site work offline
15 |
16 | if ('serviceWorker' in navigator) {
17 | navigator.serviceWorker
18 | .register('/pwa-examples/a2hs/sw.js')
19 | .then(() => { console.log('Service Worker Registered'); });
20 | }
21 |
22 | // Code to handle install prompt on desktop
23 |
24 | let deferredPrompt;
25 | const addBtn = document.querySelector('.add-button');
26 |
27 | window.addEventListener('beforeinstallprompt', (e) => {
28 | // Prevent Chrome 67 and earlier from automatically showing the prompt
29 | e.preventDefault();
30 | // Stash the event so it can be triggered later.
31 | deferredPrompt = e;
32 | // Update UI to notify the user they can add to home screen
33 | addBtn.style.display = 'block';
34 |
35 | addBtn.addEventListener('click', () => {
36 | // hide our user interface that shows our A2HS button
37 | addBtn.style.display = 'none';
38 | // Show the prompt
39 | deferredPrompt.prompt();
40 | // Wait for the user to respond to the prompt
41 | deferredPrompt.userChoice.then((choiceResult) => {
42 | if (choiceResult.outcome === 'accepted') {
43 | console.log('User accepted the A2HS prompt');
44 | } else {
45 | console.log('User dismissed the A2HS prompt');
46 | }
47 | deferredPrompt = null;
48 | });
49 | });
50 | });
51 |
--------------------------------------------------------------------------------
/a2hs/manifest.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "background_color": "purple",
3 | "description": "Shows random fox pictures. Hey, at least it isn't cats.",
4 | "display": "fullscreen",
5 | "icons": [
6 | {
7 | "src": "icon/fox-icon.png",
8 | "sizes": "192x192",
9 | "type": "image/png"
10 | }
11 | ],
12 | "name": "Awesome fox pictures",
13 | "short_name": "Foxes",
14 | "start_url": "/pwa-examples/a2hs/index.html"
15 | }
16 |
--------------------------------------------------------------------------------
/a2hs/style.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%;
3 | background: black;
4 | }
5 |
6 | body {
7 | height: inherit;
8 | margin: 0;
9 | }
10 |
11 | img {
12 | display: block;
13 | width: 100%;
14 | }
15 |
16 | .add-button {
17 | position: absolute;
18 | top: 1px;
19 | left: 1px;
20 | display: none;
21 | }
22 |
23 | @media (orientation: landscape) {
24 | img {
25 | height: 100%;
26 | object-fit: cover;
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/a2hs/sw.js:
--------------------------------------------------------------------------------
1 | self.addEventListener('install', (e) => {
2 | e.waitUntil(
3 | caches.open('fox-store').then((cache) => cache.addAll([
4 | '/pwa-examples/a2hs/',
5 | '/pwa-examples/a2hs/index.html',
6 | '/pwa-examples/a2hs/index.js',
7 | '/pwa-examples/a2hs/style.css',
8 | '/pwa-examples/a2hs/images/fox1.jpg',
9 | '/pwa-examples/a2hs/images/fox2.jpg',
10 | '/pwa-examples/a2hs/images/fox3.jpg',
11 | '/pwa-examples/a2hs/images/fox4.jpg',
12 | ])),
13 | );
14 | });
15 |
16 | self.addEventListener('fetch', (e) => {
17 | console.log(e.request.url);
18 | e.respondWith(
19 | caches.match(e.request).then((response) => response || fetch(e.request)),
20 | );
21 | });
22 |
--------------------------------------------------------------------------------
/cycletracker/html_and_css/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cycle Tracker
7 |
8 |
9 |
10 | Period tracker
11 |
27 |
28 | Past periods
29 |
30 | From 01/01/2024 to 01/06/2024
31 | From 01/29/2024 to 02/04/2024
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/cycletracker/html_and_css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 1vh 1vw;
3 | background-color: #efe;
4 | }
5 | ul,
6 | fieldset,
7 | legend {
8 | border: 1px solid;
9 | background-color: #fff;
10 | }
11 | ul {
12 | padding: 0;
13 | font-family: monospace;
14 | }
15 | li,
16 | legend {
17 | list-style-type: none;
18 | padding: 0.2em 0.5em;
19 | background-color: #cfc;
20 | }
21 | li:nth-of-type(even) {
22 | background-color: inherit;
23 | }
24 |
--------------------------------------------------------------------------------
/cycletracker/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cycle Tracker PWA
7 |
8 |
9 | CycleTracker Period tracker PWA progress
10 |
11 |
12 | Tutorial:
13 | CycleTracker: Creating your first PWA
17 |
18 | Tutorial demos:
19 |
20 | HTML and CSS
21 | Secure connection (no files)
22 |
23 | JavaScript functionality
26 |
27 | Manifest file tutorial
28 | Service worker tutorial
29 |
30 | Tutorial files:
31 |
32 |
33 | HTML and CSS
37 |
38 | Secure connection (no files)
39 |
40 | JavaScript functionality
44 |
45 |
46 | Manifest file tutorial
50 |
51 |
52 | Service worker tutorial
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/cycletracker/javascript_functionality/app.js:
--------------------------------------------------------------------------------
1 | // JS file for cycleTracker app
2 |
3 | // -------------
4 | // Variable declarations
5 | // -------------
6 | const newPeriodFormEl = document.getElementsByTagName("form")[0];
7 | const startDateInputEl = document.getElementById("start-date");
8 | const endDateInputEl = document.getElementById("end-date");
9 | const pastPeriodContainer = document.getElementById("past-periods");
10 |
11 | // Storage key is an app-wide constant
12 | const STORAGE_KEY = "period-tracker";
13 |
14 | // -------------
15 | // Event Handlers
16 | // -------------
17 | newPeriodFormEl.addEventListener("submit", (event) => {
18 | event.preventDefault();
19 | const startDate = startDateInputEl.value;
20 | const endDate = endDateInputEl.value;
21 | if (checkDatesInvalid(startDate, endDate)) {
22 | return;
23 | }
24 | storeNewPeriod(startDate, endDate);
25 | renderPastPeriods();
26 | newPeriodFormEl.reset();
27 | });
28 |
29 | // -------------
30 | // Functionality
31 | // -------------
32 |
33 | // 1. Form validation
34 | function checkDatesInvalid(startDate, endDate) {
35 | if (!startDate || !endDate || startDate > endDate) {
36 | newPeriodFormEl.reset();
37 | return true;
38 | }
39 | return false;
40 | }
41 |
42 | // 2. Get, add, sort, and store data
43 | function storeNewPeriod(startDate, endDate) {
44 | const periods = getAllStoredPeriods();
45 | periods.push({ startDate, endDate });
46 | periods.sort((a, b) => {
47 | return new Date(b.startDate) - new Date(a.startDate);
48 | });
49 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(periods));
50 | }
51 |
52 | // 3. Get and parse data
53 | function getAllStoredPeriods() {
54 | const data = window.localStorage.getItem(STORAGE_KEY);
55 | const periods = data ? JSON.parse(data) : [];
56 | return periods;
57 | }
58 |
59 | // 4. Display data
60 | function renderPastPeriods() {
61 | const pastPeriodHeader = document.createElement("h2");
62 | const pastPeriodList = document.createElement("ul");
63 | const periods = getAllStoredPeriods();
64 | if (periods.length === 0) {
65 | return;
66 | }
67 | pastPeriodContainer.innerHTML = "";
68 | pastPeriodHeader.textContent = "Past periods";
69 | periods.forEach((period) => {
70 | const periodEl = document.createElement("li");
71 | periodEl.textContent = `From ${formatDate(
72 | period.startDate,
73 | )} to ${formatDate(period.endDate)}`;
74 | pastPeriodList.appendChild(periodEl);
75 | });
76 |
77 | pastPeriodContainer.appendChild(pastPeriodHeader);
78 | pastPeriodContainer.appendChild(pastPeriodList);
79 | }
80 |
81 | // 5. format dates for display
82 | function formatDate(dateString) {
83 | const date = new Date(dateString);
84 | return date.toLocaleDateString("en-US", { timeZone: "UTC" });
85 | }
86 |
87 | // -------------
88 | // Call render on page load
89 | // -------------
90 |
91 | renderPastPeriods();
92 |
--------------------------------------------------------------------------------
/cycletracker/javascript_functionality/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cycle Tracker
7 |
8 |
9 |
10 | Period tracker
11 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/cycletracker/javascript_functionality/style.css:
--------------------------------------------------------------------------------
1 | /* CSS for cycleTracker App */
2 |
3 | body {
4 | margin: 1vh 1vw;
5 | background-color: #efe;
6 | }
7 | ul,
8 | fieldset,
9 | legend {
10 | border: 1px solid;
11 | background-color: #fff;
12 | }
13 | ul {
14 | padding: 0;
15 | font-family: monospace;
16 | }
17 | li,
18 | legend {
19 | list-style-type: none;
20 | padding: 0.2em 0.5em;
21 | background-color: #cfc;
22 | }
23 | li:nth-of-type(even) {
24 | background-color: inherit;
25 | }
26 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/app.js:
--------------------------------------------------------------------------------
1 | // JS file for cycleTracker app
2 |
3 | // -------------
4 | // Variable declarations
5 | // -------------
6 | const newPeriodFormEl = document.getElementsByTagName("form")[0];
7 | const startDateInputEl = document.getElementById("start-date");
8 | const endDateInputEl = document.getElementById("end-date");
9 | const pastPeriodContainer = document.getElementById("past-periods");
10 |
11 | // Storage key is an app-wide constant
12 | const STORAGE_KEY = "period-tracker";
13 |
14 | // -------------
15 | // Event Handlers
16 | // -------------
17 | newPeriodFormEl.addEventListener("submit", (event) => {
18 | event.preventDefault();
19 | const startDate = startDateInputEl.value;
20 | const endDate = endDateInputEl.value;
21 | if (checkDatesInvalid(startDate, endDate)) {
22 | return;
23 | }
24 | storeNewPeriod(startDate, endDate);
25 | renderPastPeriods();
26 | newPeriodFormEl.reset();
27 | });
28 |
29 | // -------------
30 | // Functionality
31 | // -------------
32 |
33 | // 1. Form validation
34 | function checkDatesInvalid(startDate, endDate) {
35 | if (!startDate || !endDate || startDate > endDate) {
36 | newPeriodFormEl.reset();
37 | return true;
38 | }
39 | return false;
40 | }
41 |
42 | // 2. Get, add, sort, and store data
43 | function storeNewPeriod(startDate, endDate) {
44 | const periods = getAllStoredPeriods();
45 | periods.push({ startDate, endDate });
46 | periods.sort((a, b) => {
47 | return new Date(b.startDate) - new Date(a.startDate);
48 | });
49 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(periods));
50 | }
51 |
52 | // 3. Get and parse data
53 | function getAllStoredPeriods() {
54 | const data = window.localStorage.getItem(STORAGE_KEY);
55 | const periods = data ? JSON.parse(data) : [];
56 | return periods;
57 | }
58 |
59 | // 4. Display data
60 | function renderPastPeriods() {
61 | const pastPeriodHeader = document.createElement("h2");
62 | const pastPeriodList = document.createElement("ul");
63 | const periods = getAllStoredPeriods();
64 | if (periods.length === 0) {
65 | return;
66 | }
67 | pastPeriodContainer.innerHTML = "";
68 | pastPeriodHeader.textContent = "Past periods";
69 | periods.forEach((period) => {
70 | const periodEl = document.createElement("li");
71 | periodEl.textContent = `From ${formatDate(
72 | period.startDate,
73 | )} to ${formatDate(period.endDate)}`;
74 | pastPeriodList.appendChild(periodEl);
75 | });
76 |
77 | pastPeriodContainer.appendChild(pastPeriodHeader);
78 | pastPeriodContainer.appendChild(pastPeriodList);
79 | }
80 |
81 | // 5. format dates for display
82 | function formatDate(dateString) {
83 | const date = new Date(dateString);
84 | return date.toLocaleDateString("en-US", { timeZone: "UTC" });
85 | }
86 |
87 | // -------------
88 | // Call render on page load
89 | // -------------
90 |
91 | renderPastPeriods();
92 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/cycletracker.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cycleTracker: Period Tracking app",
3 | "short_name": "CT",
4 | "description": "Securely and confidentially track your menstrual cycle. Enter the start and end dates of your periods, saving your private data to your browser on your device, without sharing it with the rest of the world.",
5 | "start_url": "/",
6 | "theme_color": "#eeffee",
7 | "background_color": "#eeffee",
8 | "display": "standalone",
9 | "icons": [
10 | {
11 | "src": "favicon.ico",
12 | "sizes": "48x48"
13 | },
14 | {
15 | "src": "icons/circle.svg",
16 | "sizes": "72x72 96x96",
17 | "purpose": "maskable"
18 | },
19 | {
20 | "src": "icons/tire.svg",
21 | "sizes": "128x128 256x256"
22 | },
23 | {
24 | "src": "icons/wheel.svg",
25 | "sizes": "512x512"
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/cycletracker/manifest_file/favicon.ico
--------------------------------------------------------------------------------
/cycletracker/manifest_file/icons/circle.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/cycletracker/manifest_file/icons/circle.ico
--------------------------------------------------------------------------------
/cycletracker/manifest_file/icons/circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/icons/tire.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/icons/wheel.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cycle Tracker
7 |
8 |
9 |
10 |
11 |
12 | Period tracker
13 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/cycletracker/manifest_file/style.css:
--------------------------------------------------------------------------------
1 | /* CSS for cycleTracker App */
2 |
3 | body {
4 | margin: 1vh 1vw;
5 | background-color: #efe;
6 | }
7 | ul,
8 | fieldset,
9 | legend {
10 | border: 1px solid;
11 | background-color: #fff;
12 | }
13 | ul {
14 | padding: 0;
15 | font-family: monospace;
16 | }
17 | li,
18 | legend {
19 | list-style-type: none;
20 | padding: 0.2em 0.5em;
21 | background-color: #cfc;
22 | }
23 | li:nth-of-type(even) {
24 | background-color: inherit;
25 | }
26 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/app.js:
--------------------------------------------------------------------------------
1 | // JS file for cycleTracker app
2 |
3 | // -------------
4 | // Variable declarations
5 | // -------------
6 | const newPeriodFormEl = document.getElementsByTagName("form")[0];
7 | const startDateInputEl = document.getElementById("start-date");
8 | const endDateInputEl = document.getElementById("end-date");
9 | const pastPeriodContainer = document.getElementById("past-periods");
10 |
11 | // Storage key is an app-wide constant
12 | const STORAGE_KEY = "period-tracker";
13 |
14 | // -------------
15 | // Event Handlers
16 | // -------------
17 | newPeriodFormEl.addEventListener("submit", (event) => {
18 | event.preventDefault();
19 | const startDate = startDateInputEl.value;
20 | const endDate = endDateInputEl.value;
21 | if (checkDatesInvalid(startDate, endDate)) {
22 | return;
23 | }
24 | storeNewPeriod(startDate, endDate);
25 | renderPastPeriods();
26 | newPeriodFormEl.reset();
27 | });
28 |
29 | // -------------
30 | // Functionality
31 | // -------------
32 |
33 | // 1. Form validation
34 | function checkDatesInvalid(startDate, endDate) {
35 | if (!startDate || !endDate || startDate > endDate) {
36 | newPeriodFormEl.reset();
37 | return true;
38 | }
39 | return false;
40 | }
41 |
42 | // 2. Get, add, sort, and store data
43 | function storeNewPeriod(startDate, endDate) {
44 | const periods = getAllStoredPeriods();
45 | periods.push({ startDate, endDate });
46 | periods.sort((a, b) => {
47 | return new Date(b.startDate) - new Date(a.startDate);
48 | });
49 | window.localStorage.setItem(STORAGE_KEY, JSON.stringify(periods));
50 | }
51 |
52 | // 3. Get and parse data
53 | function getAllStoredPeriods() {
54 | const data = window.localStorage.getItem(STORAGE_KEY);
55 | const periods = data ? JSON.parse(data) : [];
56 | return periods;
57 | }
58 |
59 | // 4. Display data
60 | function renderPastPeriods() {
61 | const pastPeriodHeader = document.createElement("h2");
62 | const pastPeriodList = document.createElement("ul");
63 | const periods = getAllStoredPeriods();
64 | if (periods.length === 0) {
65 | return;
66 | }
67 | pastPeriodContainer.innerHTML = "";
68 | pastPeriodHeader.textContent = "Past periods";
69 | periods.forEach((period) => {
70 | const periodEl = document.createElement("li");
71 | periodEl.textContent = `From ${formatDate(
72 | period.startDate,
73 | )} to ${formatDate(period.endDate)}`;
74 | pastPeriodList.appendChild(periodEl);
75 | });
76 |
77 | pastPeriodContainer.appendChild(pastPeriodHeader);
78 | pastPeriodContainer.appendChild(pastPeriodList);
79 | }
80 |
81 | // 5. format dates for display
82 | function formatDate(dateString) {
83 | const date = new Date(dateString);
84 | return date.toLocaleDateString("en-US", { timeZone: "UTC" });
85 | }
86 |
87 | // -------------
88 | // Call render on page load
89 | // -------------
90 |
91 | renderPastPeriods();
92 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/cycletracker.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cycleTracker: Period Tracking app",
3 | "short_name": "CT",
4 | "description": "Securely and confidentially track your menstrual cycle. Enter the start and end dates of your periods, saving your private data to your browser on your device, without sharing it with the rest of the world.",
5 | "start_url": "/",
6 | "theme_color": "#eeffee",
7 | "background_color": "#eeffee",
8 | "display": "standalone",
9 | "icons": [
10 | {
11 | "src": "favicon.ico",
12 | "sizes": "48x48"
13 | },
14 | {
15 | "src": "icons/circle.svg",
16 | "sizes": "72x72 96x96",
17 | "purpose": "maskable"
18 | },
19 | {
20 | "src": "icons/tire.svg",
21 | "sizes": "128x128 256x256"
22 | },
23 | {
24 | "src": "icons/wheel.svg",
25 | "sizes": "512x512"
26 | }
27 | ]
28 | }
29 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/cycletracker/service_workers/favicon.ico
--------------------------------------------------------------------------------
/cycletracker/service_workers/icons/circle.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/cycletracker/service_workers/icons/circle.ico
--------------------------------------------------------------------------------
/cycletracker/service_workers/icons/circle.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/icons/tire.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/icons/wheel.svg:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Cycle Tracker
7 |
8 |
9 |
10 |
11 |
12 | Period tracker
13 |
29 |
30 |
31 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/style.css:
--------------------------------------------------------------------------------
1 | /* CSS for cycleTracker App */
2 |
3 | body {
4 | margin: 1vh 1vw;
5 | background-color: #efe;
6 | }
7 | ul,
8 | fieldset,
9 | legend {
10 | border: 1px solid;
11 | background-color: #fff;
12 | }
13 | ul {
14 | padding: 0;
15 | font-family: monospace;
16 | }
17 | li,
18 | legend {
19 | list-style-type: none;
20 | padding: 0.2em 0.5em;
21 | background-color: #cfc;
22 | }
23 | li:nth-of-type(even) {
24 | background-color: inherit;
25 | }
26 |
--------------------------------------------------------------------------------
/cycletracker/service_workers/sw.js:
--------------------------------------------------------------------------------
1 | // The version of the cache.
2 | const VERSION = "v1";
3 |
4 | // The name of the cache
5 | const CACHE_NAME = `period-tracker-${VERSION}`;
6 |
7 | // The static resources that the app needs to function.
8 | const APP_STATIC_RESOURCES = [
9 | "/",
10 | "/index.html",
11 | "/app.js",
12 | "/style.css",
13 | "/icons/wheel.svg",
14 | ];
15 |
16 | // On install, cache the static resources
17 | self.addEventListener("install", (event) => {
18 | event.waitUntil(
19 | (async () => {
20 | const cache = await caches.open(CACHE_NAME);
21 | cache.addAll(APP_STATIC_RESOURCES);
22 | })()
23 | );
24 | });
25 |
26 | // delete old caches on activate
27 | self.addEventListener("activate", (event) => {
28 | event.waitUntil(
29 | (async () => {
30 | const names = await caches.keys();
31 | await Promise.all(
32 | names.map((name) => {
33 | if (name !== CACHE_NAME) {
34 | return caches.delete(name);
35 | }
36 | })
37 | );
38 | await clients.claim();
39 | })()
40 | );
41 | });
42 |
43 | // On fetch, intercept server requests
44 | // and respond with cached responses instead of going to network
45 | self.addEventListener("fetch", (event) => {
46 | // As a single page app, direct app to always go to cached home page.
47 | if (event.request.mode === "navigate") {
48 | event.respondWith(caches.match("/"));
49 | return;
50 | }
51 |
52 | // For all other requests, go to the cache first, and then the network.
53 | event.respondWith(
54 | (async () => {
55 | const cache = await caches.open(CACHE_NAME);
56 | const cachedResponse = await cache.match(event.request);
57 | if (cachedResponse) {
58 | // Return the cached response if it's available.
59 | return cachedResponse;
60 | }
61 | // If resource isn't in the cache, return a 404.
62 | return new Response(null, { status: 404 });
63 | })()
64 | );
65 | });
66 |
--------------------------------------------------------------------------------
/js13kpwa/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
--------------------------------------------------------------------------------
/js13kpwa/app.js:
--------------------------------------------------------------------------------
1 | // Generating content based on the template
2 | const template = `
3 |
4 | #POS. NAME
5 |
12 | `;
13 | let content = '';
14 | for (let i = 0; i < games.length; i++) {
15 | let entry = template.replace(/POS/g, (i + 1))
16 | .replace(/SLUG/g, games[i].slug)
17 | .replace(/NAME/g, games[i].name)
18 | .replace(/AUTHOR/g, games[i].author)
19 | .replace(/TWITTER/g, games[i].twitter)
20 | .replace(/WEBSITE/g, games[i].website)
21 | .replace(/GITHUB/g, games[i].github);
22 | entry = entry.replace(' ', '-');
23 | content += entry;
24 | }
25 | document.getElementById('content').innerHTML = content;
26 |
27 | // Registering Service Worker
28 | if ('serviceWorker' in navigator) {
29 | navigator.serviceWorker.register('/pwa-examples/js13kpwa/sw.js');
30 | }
31 |
32 | // Requesting permission for Notifications after clicking on the button
33 | const button = document.getElementById('notifications');
34 | button.addEventListener('click', () => {
35 | Notification.requestPermission().then((result) => {
36 | if (result === 'granted') {
37 | randomNotification();
38 | }
39 | });
40 | });
41 |
42 | // Setting up random Notification
43 | function randomNotification() {
44 | const randomItem = Math.floor(Math.random() * games.length);
45 | const notifTitle = games[randomItem].name;
46 | const notifBody = `Created by ${games[randomItem].author}.`;
47 | const notifImg = `data/img/${games[randomItem].slug}.jpg`;
48 | const options = {
49 | body: notifBody,
50 | icon: notifImg,
51 | };
52 | new Notification(notifTitle, options);
53 | setTimeout(randomNotification, 30000);
54 | }
55 |
56 | // Progressive loading images
57 | const imagesToLoad = document.querySelectorAll('img[data-src]');
58 | const loadImages = (image) => {
59 | image.setAttribute('src', image.getAttribute('data-src'));
60 | image.onload = () => {
61 | image.removeAttribute('data-src');
62 | };
63 | };
64 | if ('IntersectionObserver' in window) {
65 | const observer = new IntersectionObserver((items) => {
66 | items.forEach((item) => {
67 | if (item.isIntersecting) {
68 | loadImages(item.target);
69 | observer.unobserve(item.target);
70 | }
71 | });
72 | });
73 | imagesToLoad.forEach((img) => {
74 | observer.observe(img);
75 | });
76 | } else {
77 | imagesToLoad.forEach((img) => {
78 | loadImages(img);
79 | });
80 | }
81 |
--------------------------------------------------------------------------------
/js13kpwa/data/games.js:
--------------------------------------------------------------------------------
1 | var games = [
2 | {
3 | slug: 'lost-in-cyberspace',
4 | name: 'Lost in Cyberspace',
5 | author: 'Zosia and Bartek',
6 | twitter: 'bartaz',
7 | website: '',
8 | github: 'github.com/bartaz/lost-in-cyberspace',
9 | },
10 | {
11 | slug: 'vernissage',
12 | name: 'Vernissage',
13 | author: 'Platane',
14 | twitter: 'platane_',
15 | website: 'github.com/Platane',
16 | github: 'github.com/Platane/js13k-2017',
17 | },
18 | {
19 | slug: 'coconutty',
20 | name: 'Coconutty',
21 | author: 'Mary Knize',
22 | twitter: 'captainpainway',
23 | website: 'maryknize.com',
24 | github: 'github.com/captainpainway/coconutty',
25 | },
26 | {
27 | slug: 'lost-pacman',
28 | name: 'Lost Pacman',
29 | author: 'MarcGuinea',
30 | twitter: 'MarcGuineaCasas',
31 | website: 'marcguinea.com',
32 | github: 'github.com/mguinea/lost-pacman',
33 | },
34 | {
35 | slug: 'polyhedron-runner',
36 | name: 'Polyhedron Runner',
37 | author: 'Alex Swan',
38 | twitter: 'BoldBigflank',
39 | website: 'bold-it.com',
40 | github: 'github.com/BoldBigflank/js13k-polyhedron',
41 | },
42 | {
43 | slug: 'she-is-my-universe',
44 | name: 'She is my universe',
45 | author: 'Madmarcel',
46 | twitter: 'madmarcel',
47 | website: '',
48 | github: 'github.com/madmarcel/js13k2017',
49 | },
50 | {
51 | slug: 'spacewrecked',
52 | name: 'Spacewrecked',
53 | author: 'Sorskoot',
54 | twitter: 'Sorskoot',
55 | website: 'timmykokke.com',
56 | github: 'github.com/sorskoot/js13kgames_2017_Lost',
57 | },
58 | {
59 | slug: 'shifted-dimensions',
60 | name: 'Shifted Dimensions',
61 | author: 'Nylki',
62 | twitter: 'nylk',
63 | website: 'github.com/nylki',
64 | github: 'github.com/nylki/shifted-dimensions',
65 | },
66 | {
67 | slug: 'wandering-moon',
68 | name: 'Wandering Moon',
69 | author: 'Jack Greenberg',
70 | twitter: 'thprgrmmrjck',
71 | website: '',
72 | github: 'github.com/theProgrammerJack/js13k2017',
73 | },
74 | {
75 | slug: 'lost-in-guam',
76 | name: 'Lost in Guam',
77 | author: 'Kenneth Banico',
78 | twitter: 'kjdesigns671',
79 | website: '',
80 | github: 'github.com/kbanico/lost-in-guam-vr-game',
81 | },
82 | {
83 | slug: 'balloon-problems',
84 | name: 'Balloon Problems',
85 | author: 'Fasility',
86 | twitter: 'Fasility_VR',
87 | website: 'fasility.com',
88 | github: 'github.com/flowerio/balloon-problems',
89 | },
90 | {
91 | slug: 'lost-in-my-mind',
92 | name: 'Lost in my mind',
93 | author: 'Lasagne Games',
94 | twitter: 'Lazyeels',
95 | website: '',
96 | github: 'github.com/lazyeels/js13kb',
97 | },
98 | {
99 | slug: 'lost-in-the-forest-dungeon',
100 | name: 'Lost In The Forest Dungeon',
101 | author: 'Luke',
102 | twitter: 'cannl',
103 | website: 'lc-apps.co.uk',
104 | github: 'github.com/lcapps-luke/js13k-lost',
105 | },
106 | {
107 | slug: 'galacticdiamond',
108 | name: 'GalacticDiamond',
109 | author: 'Mitruska',
110 | twitter: 'mitruska_',
111 | website: '',
112 | github: 'github.com/mitruch/GalacticDiamond-JS13KGames-2017',
113 | },
114 | {
115 | slug: 'cat-meow',
116 | name: 'Cat Meow',
117 | author: 'Lislis',
118 | twitter: '',
119 | website: '',
120 | github: 'github.com/lislis/cat-meow',
121 | },
122 | {
123 | slug: 'metamorphosis',
124 | name: 'Metamorphosis',
125 | author: 'Steff and Tanyuan',
126 | twitter: '',
127 | website: '',
128 | github: 'github.com/tanyuan/metamorphosis',
129 | },
130 | {
131 | slug: 'a-snake',
132 | name: 'A-Snake',
133 | author: 'Nick Frazier',
134 | twitter: 'nrf',
135 | website: 'nickfrazier.com',
136 | github: 'github.com/fraziern/vrsnake',
137 | },
138 | {
139 | slug: 'wherewhat',
140 | name: 'Where? What?',
141 | author: '..Katu..',
142 | twitter: '',
143 | website: '',
144 | github: 'github.com/katubrd/LostVR',
145 | },
146 | {
147 | slug: 'dont-let-your-dreams-be-memes',
148 | name: 'Don\'t let your dreams be memes',
149 | author: 'Mark Vasilkov',
150 | twitter: 'mvasilkov',
151 | website: 'mvasilkov.ovh',
152 | github: 'github.com/mvasilkov/aframe13k',
153 | },
154 | {
155 | slug: 'fly-south',
156 | name: 'Fly South',
157 | author: 'Christian Paul (jaller94)',
158 | twitter: '',
159 | website: 'chrpaul.de',
160 | github: 'github.com/jaller94/fly-south',
161 | },
162 | {
163 | slug: 'prisonri0t',
164 | name: 'PrisonRi0t',
165 | author: 'Sondor',
166 | twitter: '',
167 | website: '',
168 | github: 'github.com/gabboraron/prison',
169 | },
170 | {
171 | slug: 'debriss',
172 | name: 'DebrISS',
173 | author: 'Kovolmany',
174 | twitter: '',
175 | website: '',
176 | github: 'github.com/gabboraron/iss',
177 | },
178 | {
179 | slug: 'vr-racing',
180 | name: 'VR Racing',
181 | author: 'Vedansh Bhartia and Kartikey Pandey',
182 | twitter: '',
183 | website: '',
184 | github: 'github.com/vedanshbhartia/vr_racing',
185 | },
186 | {
187 | slug: 'a-box-invaders',
188 | name: 'A-box Invaders',
189 | author: 'Felipe Do E. Santo',
190 | twitter: 'felipez3r0',
191 | website: 'hardcodigo.com.br',
192 | github: 'github.com/felipez3r0/a-box-invaders',
193 | },
194 | {
195 | slug: 'world-lost',
196 | name: 'World Lost',
197 | author: 'Ms. K. Bhuvana Meenakshi',
198 | twitter: 'bhuvanakotees1',
199 | website: 'bhuvanameenakshik.wixsite.com/bhuvanameenakshi',
200 | github: 'github.com/bhuvanameenakshi/World_Lost',
201 | },
202 | {
203 | slug: 'give-space',
204 | name: 'Give Space',
205 | author: 'Ram',
206 | twitter: 'ram_gurumukhi',
207 | website: 'gurumukhi.wordpress.com',
208 | github: 'github.com/gurumukhi/13kGiveSpaceGame',
209 | },
210 | {
211 | slug: 'lost-in-metaverse',
212 | name: 'Lost in Metaverse',
213 | author: 'Karan Ganesan',
214 | twitter: 'karanganesan',
215 | website: 'linkedin.com/in/karanganesan',
216 | github: 'github.com/karanganesan/Lost_in_Metaverse',
217 | },
218 | {
219 | slug: 'emma-3d',
220 | name: 'Emma-3D',
221 | author: 'Prateek Roushan',
222 | twitter: '',
223 | website: '',
224 | github: 'github.com/coderprateek/Emma-3D',
225 | },
226 | ];
227 |
--------------------------------------------------------------------------------
/js13kpwa/data/img/a-box-invaders.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/a-box-invaders.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/a-snake.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/a-snake.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/balloon-problems.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/balloon-problems.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/cat-meow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/cat-meow.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/coconutty.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/coconutty.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/debriss.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/debriss.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/dont-let-your-dreams-be-memes.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/dont-let-your-dreams-be-memes.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/emma-3d.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/emma-3d.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/fly-south.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/fly-south.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/galacticdiamond.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/galacticdiamond.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/give-space.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/give-space.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-in-cyberspace.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-in-cyberspace.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-in-guam.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-in-guam.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-in-metaverse.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-in-metaverse.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-in-my-mind.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-in-my-mind.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-in-the-forest-dungeon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-in-the-forest-dungeon.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/lost-pacman.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/lost-pacman.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/metamorphosis.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/metamorphosis.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/placeholder.png
--------------------------------------------------------------------------------
/js13kpwa/data/img/polyhedron-runner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/polyhedron-runner.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/prisonri0t.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/prisonri0t.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/she-is-my-universe.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/she-is-my-universe.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/shifted-dimensions.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/shifted-dimensions.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/spacewrecked.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/spacewrecked.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/vernissage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/vernissage.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/vr-racing.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/vr-racing.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/wandering-moon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/wandering-moon.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/wherewhat.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/wherewhat.jpg
--------------------------------------------------------------------------------
/js13kpwa/data/img/world-lost.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/data/img/world-lost.jpg
--------------------------------------------------------------------------------
/js13kpwa/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/favicon.ico
--------------------------------------------------------------------------------
/js13kpwa/fonts/graduate.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/fonts/graduate.eot
--------------------------------------------------------------------------------
/js13kpwa/fonts/graduate.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/fonts/graduate.ttf
--------------------------------------------------------------------------------
/js13kpwa/fonts/graduate.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/fonts/graduate.woff
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-128.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-168.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-168.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-192.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-256.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-32.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-512.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-64.png
--------------------------------------------------------------------------------
/js13kpwa/icons/icon-96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/icons/icon-96.png
--------------------------------------------------------------------------------
/js13kpwa/img/bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/img/bg.png
--------------------------------------------------------------------------------
/js13kpwa/img/js13kgames.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mdn/pwa-examples/6f7509ea99b60a31e6d54c5ebdcace9ba4f1a158/js13kpwa/img/js13kgames.png
--------------------------------------------------------------------------------
/js13kpwa/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | js13kGames A-Frame entries
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | js13kGames A-Frame entries
23 | List of games submitted to the A-Frame category in the js13kGames 2017 competition. You can fork js13kPWA on GitHub to check its source code.
24 | Request dummy notifications
25 |
26 |
27 |
30 |
31 |
--------------------------------------------------------------------------------
/js13kpwa/js13kpwa.webmanifest:
--------------------------------------------------------------------------------
1 | {
2 | "name": "js13kGames Progressive Web App",
3 | "short_name": "js13kPWA",
4 | "description": "Progressive Web App that lists games submitted to the A-Frame category in the js13kGames 2017 competition.",
5 | "icons": [
6 | {
7 | "src": "icons/icon-32.png",
8 | "sizes": "32x32",
9 | "type": "image/png"
10 | },
11 | {
12 | "src": "icons/icon-64.png",
13 | "sizes": "64x64",
14 | "type": "image/png"
15 | },
16 | {
17 | "src": "icons/icon-96.png",
18 | "sizes": "96x96",
19 | "type": "image/png"
20 | },
21 | {
22 | "src": "icons/icon-128.png",
23 | "sizes": "128x128",
24 | "type": "image/png"
25 | },
26 | {
27 | "src": "icons/icon-168.png",
28 | "sizes": "168x168",
29 | "type": "image/png"
30 | },
31 | {
32 | "src": "icons/icon-192.png",
33 | "sizes": "192x192",
34 | "type": "image/png"
35 | },
36 | {
37 | "src": "icons/icon-256.png",
38 | "sizes": "256x256",
39 | "type": "image/png"
40 | },
41 | {
42 | "src": "icons/icon-512.png",
43 | "sizes": "512x512",
44 | "type": "image/png"
45 | }
46 | ],
47 | "start_url": "/pwa-examples/js13kpwa/index.html",
48 | "display": "fullscreen",
49 | "theme_color": "#B12A34",
50 | "background_color": "#B12A34"
51 | }
--------------------------------------------------------------------------------
/js13kpwa/style.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'Graduate';
3 | font-style: normal;
4 | font-weight: 400;
5 | src: local('Graduate'),
6 | local('Graduate-Regular'),
7 | url(fonts/graduate.eot) format('embedded-opentype'),
8 | url(fonts/graduate.ttf) format('truetype'),
9 | url(fonts/graduate.woff) format('woff');
10 | }
11 | * {
12 | margin: 0;
13 | padding: 0;
14 | }
15 | html {
16 | font-size: 62.5%;
17 | }
18 | body {
19 | background: #efefef;
20 | font: normal 1.6em Helvetica, Arial, sans-serif;
21 | line-height: 1.6;
22 | color: #333;
23 | position: relative;
24 | }
25 | h1, h3 {
26 | font-family: "Graduate", "Times New Roman", Verdana;
27 | font-weight: normal;
28 | margin-top: 0;
29 | letter-spacing: -0.05em;
30 | }
31 | h1 {
32 | font-size: 2em;
33 | line-height: 1.2em;
34 | padding: 0.5em 0.3em 0.2em 0.3em;
35 | text-align: center;
36 | color: #6c6b6b;
37 | }
38 | h1:before {
39 | content: " • ";
40 | }
41 | h1:after {
42 | content: " • ";
43 | }
44 | h3 {
45 | font-size: 1.5em;
46 | line-height: 1em;
47 | padding-top: 0.2em;
48 | margin-bottom: 0.8em;
49 | }
50 | a {
51 | text-decoration: none;
52 | color: #b12a34;
53 | }
54 | header {
55 | background: #2d2d2d url(img/bg.png) repeat;
56 | border-top: 0.2em solid #a52730;
57 | border-bottom: 0.2em solid #fff;
58 | }
59 | header p {
60 | width: 90%;
61 | margin: 1em auto;
62 | position: relative;
63 | }
64 | header img {
65 | display: block;
66 | width: 50%;
67 | min-width: 150px;
68 | max-width: 295px;
69 | }
70 | .description {
71 | text-align: center;
72 | padding: 1em;
73 | }
74 | button {
75 | display: block;
76 | margin: 0 auto 2em;
77 | }
78 | main {
79 | width: 100%;
80 | background: #fafafa;
81 | }
82 | main a:hover {
83 | text-decoration: none;
84 | border-bottom: 1px solid #b12a34;
85 | }
86 | main p {
87 | padding-bottom: 1.5em;
88 | }
89 | main ul {
90 | padding: 0.2em;
91 | }
92 | main li {
93 | line-height: 1.4em;
94 | margin-left: 1em;
95 | }
96 | main img {
97 | float: right;
98 | width: 30%;
99 | min-width: 80px;
100 | max-width: 160px;
101 | padding-bottom: 1em;
102 | }
103 | article {
104 | border-top: 0.1em solid #acabab;
105 | padding: 1em;
106 | }
107 | article img[data-src] {
108 | filter: blur(0.2em);
109 | }
110 | article img {
111 | filter: blur(0em);
112 | transition: filter 0.5s;
113 | }
114 | footer {
115 | background: #2d2d2d url(img/bg.png) repeat;
116 | border-top: 0.2em solid #fff;
117 | border-bottom: 0.2em solid #a52730;
118 | }
119 | footer p {
120 | width: 90%;
121 | margin: 0 auto;
122 | color: #e2e2e2;
123 | font-size: 0.7em;
124 | padding: 2em 0;
125 | position: relative;
126 | text-align: center;
127 | line-height: 1.2;
128 | }
129 | footer a {
130 | color: #fff;
131 | font-weight: bold;
132 | }
--------------------------------------------------------------------------------
/js13kpwa/sw.js:
--------------------------------------------------------------------------------
1 | self.importScripts('data/games.js');
2 |
3 | // Files to cache
4 | const cacheName = 'js13kPWA-v1';
5 | const appShellFiles = [
6 | '/pwa-examples/js13kpwa/',
7 | '/pwa-examples/js13kpwa/index.html',
8 | '/pwa-examples/js13kpwa/app.js',
9 | '/pwa-examples/js13kpwa/style.css',
10 | '/pwa-examples/js13kpwa/fonts/graduate.eot',
11 | '/pwa-examples/js13kpwa/fonts/graduate.ttf',
12 | '/pwa-examples/js13kpwa/fonts/graduate.woff',
13 | '/pwa-examples/js13kpwa/favicon.ico',
14 | '/pwa-examples/js13kpwa/img/js13kgames.png',
15 | '/pwa-examples/js13kpwa/img/bg.png',
16 | '/pwa-examples/js13kpwa/icons/icon-32.png',
17 | '/pwa-examples/js13kpwa/icons/icon-64.png',
18 | '/pwa-examples/js13kpwa/icons/icon-96.png',
19 | '/pwa-examples/js13kpwa/icons/icon-128.png',
20 | '/pwa-examples/js13kpwa/icons/icon-168.png',
21 | '/pwa-examples/js13kpwa/icons/icon-192.png',
22 | '/pwa-examples/js13kpwa/icons/icon-256.png',
23 | '/pwa-examples/js13kpwa/icons/icon-512.png',
24 | ];
25 | const gamesImages = [];
26 | for (let i = 0; i < games.length; i++) {
27 | gamesImages.push(`data/img/${games[i].slug}.jpg`);
28 | }
29 | const contentToCache = appShellFiles.concat(gamesImages);
30 |
31 | // Installing Service Worker
32 | self.addEventListener('install', (e) => {
33 | console.log('[Service Worker] Install');
34 | e.waitUntil((async () => {
35 | const cache = await caches.open(cacheName);
36 | console.log('[Service Worker] Caching all: app shell and content');
37 | await cache.addAll(contentToCache);
38 | })());
39 | });
40 |
41 | // Fetching content using Service Worker
42 | self.addEventListener('fetch', (e) => {
43 | // Cache http and https only, skip unsupported chrome-extension:// and file://...
44 | if (!(
45 | e.request.url.startsWith('http:') || e.request.url.startsWith('https:')
46 | )) {
47 | return;
48 | }
49 |
50 | e.respondWith((async () => {
51 | const r = await caches.match(e.request);
52 | console.log(`[Service Worker] Fetching resource: ${e.request.url}`);
53 | if (r) return r;
54 | const response = await fetch(e.request);
55 | const cache = await caches.open(cacheName);
56 | console.log(`[Service Worker] Caching new resource: ${e.request.url}`);
57 | cache.put(e.request, response.clone());
58 | return response;
59 | })());
60 | });
61 |
--------------------------------------------------------------------------------