Repository: SimonDEvans/notes Branch: master Commit: dd384080decd Files: 8 Total size: 14.6 KB Directory structure: gitextract_tctamwz9/ ├── README.md ├── css/ │ └── style.css ├── index.html ├── js/ │ ├── app.js │ ├── cache-polyfill.js │ └── note-list.js ├── manifest.json └── sw.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: README.md ================================================ # Notes Notes app Notes app PWA score A simple, offline-based notepad, based on usage of [Service Worker](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API) and [PWA techniques.](https://www.smashingmagazine.com/2016/08/a-beginners-guide-to-progressive-web-apps/) View the demo [here](https://sii.im/playground/notes) on a [supported device.](http://caniuse.com/#feat=serviceworkers) ## Features - Write notes and save them to [localStorage](https://developer.mozilla.org/en/docs/Web/API/Window/localStorage) - Use [Service Worker](https://developers.google.com/web/fundamentals/getting-started/primers/service-workers) to enable offline viewing - "Add To Home Screen" feature on both Android and iOS supported devices ================================================ FILE: css/style.css ================================================ * { box-sizing: border-box; margin: 0; padding: 0; } html, body { font-family: Helvetica Neue, sans-serif; } body { height: 100vh; background: linear-gradient(to left, #B24592 , #F15F79); } @media screen and (max-width: 500px) { body { height: auto; min-height: 100vh; background: none; } } @keyframes fadein { from { opacity: 0; transform: translate3d(0, 10px, 0); } to { opacity: 1; transform: translate3d(0, 0, 0); } } .wrapper { position: absolute; top: 0; left: 0; right: 0; bottom: 0; margin: auto; width: 400px; height: 620px; background-color: #fff; overflow: hidden; border-radius: 5px; box-shadow: 0 20px 80px rgba(0, 0, 0, 0.6); animation: fadein 1s; } @media screen and (max-width: 500px) { .wrapper { width: 100%; height: 100vh; margin: 0; overflow: scroll; box-shadow: none; animation: none; } } .wrapper__inner { position: relative; width: 100%; height: 100%; } @media screen and (max-width: 500px) { .notepad { padding: 15px; } } .notepad__heading { width: 100%; padding: 60px 40px 15px; border-bottom: 1px solid #f5f4f4; color: #5f5f5f; font-weight: 400; } .notepad__form-label { clip: rect(1px, 1px, 1px, 1px); height: 1px; overflow: hidden; position: absolute; width: 1px; } .notepad__form-input { border: 0; width: 100%; line-height: 1.3; margin: 0; font-size: 19px; color: #5f5f5f; padding: 30px 40px; background-color: transparent; transition: background-color 300ms ease; @media screen and (max-width: 500px) { .notepad__form-input { height: auto; } } } .notepad__form-input:focus { outline: 0; background-color: #f5f5f5; } .notepad__list { height: 365px; overflow: scroll; } @media screen and (max-width: 500px) { .notepad__list { height: auto; } } .notepad__list-item { width: 100%; padding: 30px 40px; border: 0; border-bottom: 1px solid #f5f4f4; line-height: 1.3; font-size: 19px; list-style-type: none; color: #5f5f5f; background-color: transparent; } .notepad__list-item:first-of-type { border-top: 1px solid #f5f4f4; } .notepad__clear { position: absolute; bottom: 0; display: none; width: 100%; padding: 20px; border: 0; font-size: 12px; text-transform: uppercase; background-color: #fff; color: #333; cursor: pointer; font-family: Helvetica Neue, sans-serif; letter-spacing: 2px; transition: color 300ms ease, background-color 300ms ease; } .notepad__clear:focus { outline: 0; } .notepad__clear:hover { color: #fff; background-color: #ea3860; } .notepad__clear--display { display: block; } @media screen and (max-width: 500px) { .notepad__clear { position: static; } } .info, .twitter { position: fixed; top: 0; right: 0; padding: 15px 15px 12px 15px; transition: background-color 300ms ease; } .info span, .twitter span { display: none; } @media screen and (max-width: 500px) { .info, .twitter { position: absolute; z-index: 2; } } @media screen and (max-width: 500px) { .info { right: 40px; } } .twitter { top: 45px; } @media screen and (max-width: 500px) { .twitter { top: 0; } } .info svg, .twitter svg { width: 30px; height: 30px; fill: #fff; transition: fill 300ms ease; } @media screen and (max-width: 500px) { .info svg, .twitter svg { width: 20px; fill: #333; } } .info:hover svg, .twitter:hover svg { fill: #333; } ================================================ FILE: index.html ================================================ Notes GitHub Twitter

Notes

    ================================================ FILE: js/app.js ================================================ // Registering ServiceWorker if ( 'serviceWorker' in navigator ) { navigator.serviceWorker.register( 'sw.js' ).then(function(registration) { // Registration was successful console.log( 'ServiceWorker registration successful. Scope: ' + registration.scope ) }).catch(function(err) { // Registration failed with error console.log( 'ServiceWorker registration failed. Error: ' + err); }); } ================================================ FILE: js/cache-polyfill.js ================================================ /** * Copyright 2015 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ (function() { var nativeAddAll = Cache.prototype.addAll; var userAgent = navigator.userAgent.match(/(Firefox|Chrome)\/(\d+\.)/); // Has nice behavior of `var` which everyone hates if (userAgent) { var agent = userAgent[1]; var version = parseInt(userAgent[2]); } if ( nativeAddAll && (!userAgent || (agent === 'Firefox' && version >= 46) || (agent === 'Chrome' && version >= 50) ) ) { return; } Cache.prototype.addAll = function addAll(requests) { var cache = this; // Since DOMExceptions are not constructable: function NetworkError(message) { this.name = 'NetworkError'; this.code = 19; this.message = message; } NetworkError.prototype = Object.create(Error.prototype); return Promise.resolve().then(function() { if (arguments.length < 1) throw new TypeError(); // Simulate sequence<(Request or USVString)> binding: var sequence = []; requests = requests.map(function(request) { if (request instanceof Request) { return request; } else { return String(request); // may throw TypeError } }); return Promise.all( requests.map(function(request) { if (typeof request === 'string') { request = new Request(request); } var scheme = new URL(request.url).protocol; if (scheme !== 'http:' && scheme !== 'https:') { throw new NetworkError("Invalid scheme"); } return fetch(request.clone()); }) ); }).then(function(responses) { // If some of the responses has not OK-eish status, // then whole operation should reject if (responses.some(function(response) { return !response.ok; })) { throw new NetworkError('Incorrect response status'); } // TODO: check that requests don't overwrite one another // (don't think this is possible to polyfill due to opaque responses) return Promise.all( responses.map(function(response, i) { return cache.put(requests[i], response); }) ); }).then(function() { return undefined; }); }; Cache.prototype.add = function add(request) { return this.addAll([request]); }; }()); ================================================ FILE: js/note-list.js ================================================ $(document).ready(function () { var noteList = function() { var $notepad = $(' .notepad' ), $noteList = $(' .notepad__list' ), $noteListItem = $( '.notepad__list-item' ), $noteForm = $( '.notepad__form' ), $noteFormInput = $( '.notepad__form-input' ), $clearList = $( '.notepad__clear' ), clearListDisplay = 'notepad__clear--display', noteCount = 0; function displayNotes() { for (noteCount = 0; noteCount < localStorage.length; noteCount++) { var noteID = 'task-' + noteCount; // Build note list $noteList.append("
  • " + localStorage.getItem(noteID) + "
  • "); // Show reset button $clearList.addClass( clearListDisplay ); } } function storeNote() { if ( $noteFormInput.val() !== '' ) { var noteID = 'task-' + noteCount, task = $( '#' + noteID ), taskMessage = $noteFormInput.val(); localStorage.setItem( noteID, taskMessage ); // Add to note list $noteList.append( "
  • " + taskMessage + "
  • " ); // Display reset button if ( !$clearList.hasClass( clearListDisplay ) ) { $clearList.addClass( clearListDisplay ); } // Reset $noteFormInput.val(''); noteCount++; } } function clearNotes() { // Update DOM $noteList.empty(); $clearList.removeClass( clearListDisplay ); // Clear storage localStorage.clear(); noteCount = 0; } function bindEvents() { // Show any existing notes from localStorage displayNotes(); // Create new note $noteForm.on( 'submit', function () { storeNote(); return false; }); // Reset notes $clearList.on( 'click', function () { clearNotes(); }); } bindEvents(); }; noteList(); }); ================================================ FILE: manifest.json ================================================ { "short_name": "notes", "name": "notes", "description" : "An offline-capable notes app, using localStorage and ServiceWorker", "display": "standalone", "orientation": "portrait", "start_url": "index.html", "theme_color": "#ffffff", "background_color": "#ffffff", "icons": [ { "src": "img/icon-60.png", "sizes": "48x48", "type": "image/png" }, { "src": "img/icon-114.png", "sizes": "114x114", "type": "image/png" }, { "src": "img/icon-152.png", "sizes": "152x152", "type": "image/png" }, { "src": "img/icon-558.png", "sizes": "558x558", "type": "image/png" } ] } ================================================ FILE: sw.js ================================================ // Polyfill for Chrome caching importScripts('js/cache-polyfill.js'); // Install the ServiceWorker self.addEventListener('install', function(event) { event.waitUntil( // Open a cache caches.open('v1').then(function(cache) { // Define what we want to cache return cache.addAll([ '/', 'index.html', 'js/app.js', 'js/jquery.min.js', 'js/note-list.js', 'css/style.css', 'favicon.ico', 'manifest.json', 'img/icon-60.png', 'img/icon-114.png', 'img/icon-152.png', 'img/icon-558.png' ]); }) ); }); // Use ServiceWorker (or not) to fetch data self.addEventListener('fetch', function(event) { event.respondWith( // Look for something in the cache that matches the request caches.match(event.request).then(function(response) { // If we find something, return it // Otherwise, use the network instead return response || fetch(event.request); }) ); });