├── step1 ├── favicon.ico ├── images │ ├── fog.png │ ├── rain.png │ ├── snow.png │ ├── wind.png │ ├── clear.png │ ├── cloudy.png │ ├── sleet.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── thunderstorm.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── index.html ├── scripts │ ├── app.js │ └── push-client.js └── styles │ └── inline.css ├── step2 ├── favicon.ico ├── images │ ├── fog.png │ ├── rain.png │ ├── snow.png │ ├── wind.png │ ├── clear.png │ ├── cloudy.png │ ├── sleet.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── thunderstorm.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── index.html ├── scripts │ ├── app.js │ └── push-client.js └── styles │ └── inline.css ├── step3 ├── favicon.ico ├── images │ ├── fog.png │ ├── rain.png │ ├── snow.png │ ├── wind.png │ ├── clear.png │ ├── cloudy.png │ ├── sleet.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── thunderstorm.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── index.html ├── service-worker.js ├── scripts │ ├── app.js │ └── push-client.js └── styles │ └── inline.css ├── step4 ├── favicon.ico ├── images │ ├── fog.png │ ├── rain.png │ ├── snow.png │ ├── wind.png │ ├── clear.png │ ├── cloudy.png │ ├── sleet.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── thunderstorm.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── index.html ├── service-worker.js ├── scripts │ ├── app.js │ └── push-client.js └── styles │ └── inline.css ├── step5 ├── favicon.ico ├── images │ ├── fog.png │ ├── rain.png │ ├── snow.png │ ├── wind.png │ ├── clear.png │ ├── cloudy.png │ ├── sleet.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── thunderstorm.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── manifest.json ├── index.html ├── service-worker.js ├── scripts │ ├── app.js │ └── push-client.js └── styles │ └── inline.css ├── README.md ├── step6-complete ├── favicon.ico ├── images │ ├── clear.png │ ├── fog.png │ ├── rain.png │ ├── sleet.png │ ├── snow.png │ ├── wind.png │ ├── cloudy.png │ ├── thunderstorm.png │ ├── cloudy_s_sunny.png │ ├── partly-cloudy.png │ ├── icons │ │ ├── icon-32x32.png │ │ ├── icon-128x128.png │ │ ├── icon-144x144.png │ │ ├── icon-152x152.png │ │ ├── icon-192x192.png │ │ └── icon-256x256.png │ ├── scattered-showers.png │ ├── cloudy-scattered-showers.png │ ├── ic_notifications_black_24px.svg │ └── ic_notifications_white_24px.svg ├── manifest.json ├── index.html ├── service-worker.js ├── scripts │ └── app.js └── styles │ └── inline.css ├── package.json ├── .gitignore └── server.js /step1/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/favicon.ico -------------------------------------------------------------------------------- /step2/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/favicon.ico -------------------------------------------------------------------------------- /step3/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/favicon.ico -------------------------------------------------------------------------------- /step4/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/favicon.ico -------------------------------------------------------------------------------- /step5/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/favicon.ico -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mobile Web Summit Workshop 2 | 3 | Instructions for the workshop - https://goo.gl/hlMfS2 4 | -------------------------------------------------------------------------------- /step1/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/fog.png -------------------------------------------------------------------------------- /step1/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/rain.png -------------------------------------------------------------------------------- /step1/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/snow.png -------------------------------------------------------------------------------- /step1/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/wind.png -------------------------------------------------------------------------------- /step2/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/fog.png -------------------------------------------------------------------------------- /step2/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/rain.png -------------------------------------------------------------------------------- /step2/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/snow.png -------------------------------------------------------------------------------- /step2/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/wind.png -------------------------------------------------------------------------------- /step3/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/fog.png -------------------------------------------------------------------------------- /step3/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/rain.png -------------------------------------------------------------------------------- /step3/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/snow.png -------------------------------------------------------------------------------- /step3/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/wind.png -------------------------------------------------------------------------------- /step4/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/fog.png -------------------------------------------------------------------------------- /step4/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/rain.png -------------------------------------------------------------------------------- /step4/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/snow.png -------------------------------------------------------------------------------- /step4/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/wind.png -------------------------------------------------------------------------------- /step5/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/fog.png -------------------------------------------------------------------------------- /step5/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/rain.png -------------------------------------------------------------------------------- /step5/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/snow.png -------------------------------------------------------------------------------- /step5/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/wind.png -------------------------------------------------------------------------------- /step1/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/clear.png -------------------------------------------------------------------------------- /step1/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/cloudy.png -------------------------------------------------------------------------------- /step1/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/sleet.png -------------------------------------------------------------------------------- /step2/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/clear.png -------------------------------------------------------------------------------- /step2/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/cloudy.png -------------------------------------------------------------------------------- /step2/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/sleet.png -------------------------------------------------------------------------------- /step3/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/clear.png -------------------------------------------------------------------------------- /step3/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/cloudy.png -------------------------------------------------------------------------------- /step3/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/sleet.png -------------------------------------------------------------------------------- /step4/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/clear.png -------------------------------------------------------------------------------- /step4/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/cloudy.png -------------------------------------------------------------------------------- /step4/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/sleet.png -------------------------------------------------------------------------------- /step5/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/clear.png -------------------------------------------------------------------------------- /step5/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/cloudy.png -------------------------------------------------------------------------------- /step5/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/sleet.png -------------------------------------------------------------------------------- /step6-complete/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/favicon.ico -------------------------------------------------------------------------------- /step1/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step1/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/partly-cloudy.png -------------------------------------------------------------------------------- /step1/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/thunderstorm.png -------------------------------------------------------------------------------- /step2/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step2/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/partly-cloudy.png -------------------------------------------------------------------------------- /step2/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/thunderstorm.png -------------------------------------------------------------------------------- /step3/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step3/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/partly-cloudy.png -------------------------------------------------------------------------------- /step3/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/thunderstorm.png -------------------------------------------------------------------------------- /step4/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step4/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/partly-cloudy.png -------------------------------------------------------------------------------- /step4/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/thunderstorm.png -------------------------------------------------------------------------------- /step5/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step5/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/partly-cloudy.png -------------------------------------------------------------------------------- /step5/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/thunderstorm.png -------------------------------------------------------------------------------- /step6-complete/images/clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/clear.png -------------------------------------------------------------------------------- /step6-complete/images/fog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/fog.png -------------------------------------------------------------------------------- /step6-complete/images/rain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/rain.png -------------------------------------------------------------------------------- /step6-complete/images/sleet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/sleet.png -------------------------------------------------------------------------------- /step6-complete/images/snow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/snow.png -------------------------------------------------------------------------------- /step6-complete/images/wind.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/wind.png -------------------------------------------------------------------------------- /step1/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step2/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step3/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step4/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step5/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step6-complete/images/cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/cloudy.png -------------------------------------------------------------------------------- /step1/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step1/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step1/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step1/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step1/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step1/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/scattered-showers.png -------------------------------------------------------------------------------- /step2/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step2/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step2/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step2/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step2/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step2/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/scattered-showers.png -------------------------------------------------------------------------------- /step3/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step3/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step3/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step3/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step3/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step3/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/scattered-showers.png -------------------------------------------------------------------------------- /step4/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step4/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step4/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step4/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step4/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step4/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/scattered-showers.png -------------------------------------------------------------------------------- /step5/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step5/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step5/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step5/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step5/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step5/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/scattered-showers.png -------------------------------------------------------------------------------- /step6-complete/images/thunderstorm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/thunderstorm.png -------------------------------------------------------------------------------- /step1/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step1/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step2/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step2/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step3/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step3/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step4/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step4/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step5/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step5/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step6-complete/images/cloudy_s_sunny.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/cloudy_s_sunny.png -------------------------------------------------------------------------------- /step6-complete/images/partly-cloudy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/partly-cloudy.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-32x32.png -------------------------------------------------------------------------------- /step6-complete/images/scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/scattered-showers.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-128x128.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-144x144.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-152x152.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-192x192.png -------------------------------------------------------------------------------- /step6-complete/images/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/icons/icon-256x256.png -------------------------------------------------------------------------------- /step6-complete/images/cloudy-scattered-showers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/owencm/ModernWebSummitWorkshop/HEAD/step6-complete/images/cloudy-scattered-showers.png -------------------------------------------------------------------------------- /step1/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step1/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step2/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step2/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step3/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step3/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step4/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step4/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step5/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step5/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step6-complete/images/ic_notifications_black_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /step6-complete/images/ic_notifications_white_24px.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ModernWebSummitWorkshop", 3 | "version": "1.0.0", 4 | "private": "true", 5 | "description": "", 6 | "main": "server.js", 7 | "scripts": { 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "MIT", 13 | "bugs": { 14 | "url": "https://github.com/owencm/ModernWebSummitWorkshop/issues" 15 | }, 16 | "homepage": "https://github.com/owencm/ModernWebSummitWorkshop#readme", 17 | "dependencies": { 18 | "body-parser": "^1.14.2", 19 | "express": "^4.13.4", 20 | "sw-toolbox": "^3.1.1", 21 | "web-push": "^1.0.2" 22 | }, 23 | "devDependencies": { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | .DS_STORE 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | 9 | # Runtime data 10 | pids 11 | *.pid 12 | *.seed 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directory 30 | node_modules 31 | 32 | # Optional npm cache directory 33 | .npm 34 | 35 | # Optional REPL history 36 | .node_repl_history 37 | -------------------------------------------------------------------------------- /step5/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Weather", 3 | "short_name": "Weather", 4 | "icons": [{ 5 | "src": "images/icons/icon-128x128.png", 6 | "sizes": "128x128", 7 | "type": "image/png" 8 | }, { 9 | "src": "images/icons/icon-144x144.png", 10 | "sizes": "144x144", 11 | "type": "image/png" 12 | }, { 13 | "src": "images/icons/icon-152x152.png", 14 | "sizes": "152x152", 15 | "type": "image/png" 16 | }, { 17 | "src": "images/icons/icon-192x192.png", 18 | "sizes": "192x192", 19 | "type": "image/png" 20 | }, { 21 | "src": "images/icons/icon-256x256.png", 22 | "sizes": "256x256", 23 | "type": "image/png" 24 | }], 25 | "start_url": "index.html?homescreen=1", 26 | "display": "standalone", 27 | "background_color": "#3E4EB8", 28 | "theme_color": "#2F3BA2", 29 | "gcm_sender_id": "70689946818" 30 | } 31 | -------------------------------------------------------------------------------- /step6-complete/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Weather", 3 | "short_name": "Weather", 4 | "icons": [{ 5 | "src": "images/icons/icon-128x128.png", 6 | "sizes": "128x128", 7 | "type": "image/png" 8 | }, { 9 | "src": "images/icons/icon-144x144.png", 10 | "sizes": "144x144", 11 | "type": "image/png" 12 | }, { 13 | "src": "images/icons/icon-152x152.png", 14 | "sizes": "152x152", 15 | "type": "image/png" 16 | }, { 17 | "src": "images/icons/icon-192x192.png", 18 | "sizes": "192x192", 19 | "type": "image/png" 20 | }, { 21 | "src": "images/icons/icon-256x256.png", 22 | "sizes": "256x256", 23 | "type": "image/png" 24 | }], 25 | "start_url": "index.html?homescreen=1", 26 | "display": "standalone", 27 | "background_color": "#3E4EB8", 28 | "theme_color": "#2F3BA2", 29 | "gcm_sender_id": "70689946818" 30 | } 31 | -------------------------------------------------------------------------------- /step1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step3/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step6-complete/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Weather PWA 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 |

Weather PWA

21 | 22 |
23 | 24 | 29 | 30 |
31 | 32 | 33 | 34 |
35 | 36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /step5/service-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('../node_modules/sw-toolbox/sw-toolbox.js'); 2 | var version = '6'; 3 | var dataCacheName = 'weatherData-v'+version; 4 | var cacheName = 'weatherPWA-step-celebrate-'+version; 5 | var filesToCache = [ 6 | './', 7 | './index.html', 8 | './scripts/app.js', 9 | './styles/inline.css', 10 | './images/icons/icon-256x256.png' 11 | ]; 12 | 13 | toolbox.options.cache.name = cacheName; 14 | toolbox.precache(filesToCache); 15 | 16 | self.addEventListener('install', function(e) { 17 | e.waitUntil(self.skipWaiting()); 18 | }); 19 | 20 | // activate event 21 | self.addEventListener('activate', function(e) { 22 | console.log('[ServiceWorker] Activate'); 23 | e.waitUntil( 24 | caches.keys().then(function(keyList) { 25 | return Promise.all(keyList.map(function(key) { 26 | console.log('[ServiceWorker] Removing old cache', key); 27 | if ((key !== dataCacheName || key !== cacheName) && key.indexOf("$$$inactive$$$") === -1) { 28 | return caches.delete(key); 29 | } 30 | })); 31 | }) 32 | ); 33 | }); 34 | 35 | var nosw = 0; 36 | self.addEventListener('fetch', function(e) { 37 | var url = new URL(e.request.url); 38 | if (nosw || (url.search.indexOf("nosw=1") >= 0)) { 39 | nosw = 1; 40 | return; 41 | } 42 | }); 43 | 44 | toolbox.router.get('/(.*)', toolbox.cacheFirst, { 45 | cache: { name: cacheName } 46 | }); 47 | 48 | // You will use this later to set up push notifications 49 | // self.addEventListener('push', function(e) { 50 | // console.log('[ServiceWorker] Received push event'); 51 | // }); 52 | -------------------------------------------------------------------------------- /step4/service-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('../node_modules/sw-toolbox/sw-toolbox.js'); 2 | var version = '6'; 3 | var dataCacheName = 'weatherData-v'+version; 4 | var cacheName = 'weatherPWA-step-celebrate-'+version; 5 | var filesToCache = [ 6 | './', 7 | './index.html', 8 | './scripts/app.js', 9 | './styles/inline.css', 10 | './images/icons/icon-256x256.png' 11 | ]; 12 | 13 | toolbox.options.cache.name = cacheName; 14 | toolbox.precache(filesToCache); 15 | 16 | self.addEventListener('install', function(e) { 17 | e.waitUntil(self.skipWaiting()); 18 | }); 19 | 20 | // activate event 21 | self.addEventListener('activate', function(e) { 22 | console.log('[ServiceWorker] Activate'); 23 | e.waitUntil( 24 | caches.keys().then(function(keyList) { 25 | return Promise.all(keyList.map(function(key) { 26 | console.log('[ServiceWorker] Removing old cache', key); 27 | if ((key !== dataCacheName || key !== cacheName) && key.indexOf("$$$inactive$$$") === -1) { 28 | return caches.delete(key); 29 | } 30 | })); 31 | }) 32 | ); 33 | }); 34 | 35 | var nosw = 0; 36 | self.addEventListener('fetch', function(e) { 37 | var url = new URL(e.request.url); 38 | if (nosw || (url.search.indexOf("nosw=1") >= 0)) { 39 | nosw = 1; 40 | return; 41 | } 42 | }); 43 | 44 | toolbox.router.get('/data/(.*)', toolbox.networkFirst, { 45 | cache: { name: dataCacheName } 46 | }); 47 | 48 | toolbox.router.default = toolbox.cacheFirst; 49 | 50 | // You will use this later to set up push notifications 51 | // self.addEventListener('push', function(e) { 52 | // console.log('[ServiceWorker] Received push event'); 53 | // }); 54 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | var express = require('express'); 2 | var app = express(); 3 | var bodyParser = require('body-parser'); 4 | var jsonParser = bodyParser.json(); 5 | var PORT = process.env.PORT || 8000; 6 | var webPush = require('web-push'); 7 | 8 | // The GCM API key is AIzaSyDNlm9R_w_0FDGjSM1fzyx5I5JnJBXACqU 9 | webPush.setGCMAPIKey('AIzaSyDNlm9R_w_0FDGjSM1fzyx5I5JnJBXACqU'); 10 | 11 | app.use(express.static(__dirname)); 12 | 13 | app.use(bodyParser.json()); 14 | 15 | app.use("/push", function(req, res, next) { 16 | console.log(res.body); 17 | if (req.body.action === 'subscribe') { 18 | var endpoint = req.body.subscription.endpoint; 19 | 20 | // Send a push once immediately after subscribing, and one 5 seconds later 21 | sendNotification(endpoint); 22 | setTimeout(function() { 23 | console.log("[[SENDING PUSH NOW: " + endpoint + "]]"); 24 | sendNotification(endpoint); 25 | }, 5000); 26 | res.send({text:'Sending push in 5',status:"200"}); 27 | } else { 28 | throw new Error ('Unsupported action'); 29 | } 30 | }); 31 | 32 | app.use("/pushdata",function(req,res,next){ 33 | res.send(JSON.stringify({ 34 | msg: "We have "+parseInt(Math.random()*9+1)+" cabs available for you for next one hour" 35 | })); 36 | }); 37 | 38 | app.listen(PORT, 'localhost', function() { 39 | console.log('express server listening on port', PORT); 40 | }); 41 | 42 | function sendNotification(endpoint) { 43 | console.log('endpoint', endpoint) 44 | webPush.sendNotification(endpoint) 45 | .then(function(response) { 46 | if (response) { 47 | console.log("PUSH RESPONSE: ", response); 48 | } else { 49 | console.log("PUSH SENT"); 50 | } 51 | }) 52 | .catch(function(err) { 53 | console.error("PUSH ERR: " + err); 54 | }); 55 | } 56 | -------------------------------------------------------------------------------- /step6-complete/service-worker.js: -------------------------------------------------------------------------------- 1 | importScripts('../node_modules/sw-toolbox/sw-toolbox.js'); 2 | var version = '6'; 3 | var dataCacheName = 'weatherData-v'+version; 4 | var cacheName = 'weatherPWA-step-celebrate-'+version; 5 | var filesToCache = [ 6 | './', 7 | './index.html', 8 | './scripts/app.js', 9 | './styles/inline.css', 10 | './images/icons/icon-256x256.png' 11 | ]; 12 | 13 | toolbox.options.cache.name = cacheName; 14 | toolbox.precache(filesToCache); 15 | 16 | self.addEventListener('install', function(e) { 17 | e.waitUntil(self.skipWaiting()); 18 | }); 19 | 20 | // activate event 21 | self.addEventListener('activate', function(e) { 22 | console.log('[ServiceWorker] Activate'); 23 | e.waitUntil( 24 | caches.keys().then(function(keyList) { 25 | return Promise.all(keyList.map(function(key) { 26 | console.log('[ServiceWorker] Removing old cache', key); 27 | if ((key !== dataCacheName || key !== cacheName) && key.indexOf("$$$inactive$$$") === -1) { 28 | return caches.delete(key); 29 | } 30 | })); 31 | }) 32 | ); 33 | }); 34 | 35 | var nosw = 0; 36 | self.addEventListener('fetch', function(e) { 37 | var url = new URL(e.request.url); 38 | if (nosw || (url.search.indexOf("nosw=1") >= 0)) { 39 | nosw = 1; 40 | return; 41 | } 42 | }); 43 | 44 | self.addEventListener('push', function(e) { 45 | console.log('[ServiceWorker] Received push event'); 46 | e.waitUntil( 47 | fetch('/pushdata').then(function(response) { 48 | return response.json(); 49 | }).then(function(data) { 50 | var title = 'Weather PWA'; 51 | var body = data.msg; 52 | var icon = '/images/icons/icon-256x256.png'; 53 | var tag = 'static-tag'; 54 | return self.registration.showNotification(title, { 55 | body: body, 56 | icon: icon, 57 | tag: tag 58 | }); 59 | }, function(err) { 60 | console.error(err); 61 | }) 62 | ); 63 | }); 64 | 65 | toolbox.router.get('/(.*)', toolbox.cacheFirst, { 66 | cache: { name: cacheName } 67 | }); 68 | -------------------------------------------------------------------------------- /step3/service-worker.js: -------------------------------------------------------------------------------- 1 | var version = '5'; 2 | var dataCacheName = 'weatherData-v' + version; 3 | var cacheName = 'weatherPWA-v' + version; 4 | var filesToCache = [ 5 | './', 6 | './index.html', 7 | './scripts/app.js', 8 | './images/icons/icon-256x256.png' 9 | ]; 10 | 11 | self.addEventListener('install', (e) => { 12 | console.log('[ServiceWorker] Install'); 13 | e.waitUntil( 14 | caches.open(cacheName).then((cache) => { 15 | console.log('[ServiceWorker] Caching App Shell'); 16 | return cache.addAll(filesToCache); 17 | }) 18 | ); 19 | }); 20 | 21 | self.addEventListener('activate', (e) => { 22 | console.log('[ServiceWorker] Activate'); 23 | e.waitUntil( 24 | caches.keys().then((keyList) => { 25 | return Promise.all(keyList.map((key) => { 26 | console.log('[ServiceWorker] Removing old cache', key); 27 | if (key !== cacheName) { 28 | return caches.delete(key); 29 | } 30 | })); 31 | }) 32 | ); 33 | }); 34 | 35 | var nosw = 0; 36 | self.addEventListener('fetch', (e) => { 37 | var url = new URL(e.request.url); 38 | if(nosw || (url.search.indexOf("nosw=1") >= 0)) { 39 | console.log('[ServiceWorker] Skipping the ServiceWorker'); 40 | nosw = 1; 41 | return; // Fall through to the network 42 | } 43 | 44 | console.log('[ServiceWorker] Fetch', e.request.url); 45 | if (e.request.url.indexOf('data/') != -1) { 46 | e.respondWith( 47 | fetch(e.request) 48 | .then((response) => { 49 | return caches.open(dataCacheName).then((cache) => { 50 | cache.put(e.request.url, response.clone()); 51 | console.log('[ServiceWorker] Fetched & Cached', e.request.url); 52 | return response; 53 | }); 54 | }) 55 | ); 56 | } else { 57 | e.respondWith( 58 | caches.match(e.request).then((response) => { 59 | return response || fetch(e.request); 60 | }) 61 | ); 62 | } 63 | }); 64 | 65 | // You will use this later to set up push notifications 66 | // self.addEventListener('push', function(e) { 67 | // console.log('[ServiceWorker] Received push event'); 68 | // }); 69 | -------------------------------------------------------------------------------- /step2/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | 55 | }); 56 | 57 | /***************************************************************************** 58 | * 59 | * Methods for dealing with the network 60 | * 61 | ****************************************************************************/ 62 | 63 | // Gets a forecast for a specific city 64 | app.getForecast = function(cityKey) { 65 | var url = '../data/' + cityKey + '.json'; 66 | app.hasRequestPending = true; 67 | // Make the XHR to get the data, then update the card 68 | return fetch(url).then((response) => { 69 | if (response.status === 200) { 70 | return response.json(); 71 | } 72 | }).then((data) => { 73 | app.hasRequestPending = false; 74 | return data; 75 | }); 76 | }; 77 | 78 | /***************************************************************************** 79 | * 80 | * Start the app 81 | * 82 | ****************************************************************************/ 83 | 84 | app.selectedCityKeys = [ 85 | 'austin', 86 | 'baltimore', 87 | 'boston', 88 | 'chicago', 89 | 'dallas', 90 | 'losangeles', 91 | 'newyork', 92 | 'sanfrancisco', 93 | ]; 94 | 95 | app.selectedCityKeys.forEach((cityKey) => { 96 | app.getForecast(cityKey).then((forecast) => { 97 | app.updateForecastCard(cityKey, forecast); 98 | }); 99 | }); 100 | -------------------------------------------------------------------------------- /step1/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | 55 | }); 56 | 57 | /***************************************************************************** 58 | * 59 | * Methods for dealing with the network 60 | * 61 | ****************************************************************************/ 62 | 63 | // Gets a forecast for a specific city 64 | app.getForecast = function(cityKey) { 65 | var url = '../data/' + cityKey + '.json'; 66 | app.hasRequestPending = true; 67 | // Make the XHR to get the data, then update the card 68 | return fetch(url).then((response) => { 69 | if (response.status === 200) { 70 | return response.json(); 71 | } 72 | }).then((data) => { 73 | app.hasRequestPending = false; 74 | console.log('[App] Forecast Updated From Network'); 75 | return data; 76 | }); 77 | }; 78 | 79 | /***************************************************************************** 80 | * 81 | * Start the app 82 | * 83 | ****************************************************************************/ 84 | 85 | app.selectedCityKeys = [ 86 | 'austin', 87 | 'baltimore', 88 | 'boston', 89 | 'chicago', 90 | 'dallas', 91 | 'losangeles', 92 | 'newyork', 93 | 'sanfrancisco', 94 | ]; 95 | 96 | app.selectedCityKeys.forEach((cityKey) => { 97 | app.getForecast(cityKey).then((forecast) => { 98 | app.updateForecastCard(cityKey, forecast); 99 | }); 100 | }); 101 | -------------------------------------------------------------------------------- /step3/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | 55 | }); 56 | 57 | /***************************************************************************** 58 | * 59 | * Methods for dealing with the network 60 | * 61 | ****************************************************************************/ 62 | 63 | // Gets a forecast for a specific city 64 | app.getForecast = function(cityKey) { 65 | var url = '../data/' + cityKey + '.json'; 66 | app.hasRequestPending = true; 67 | // Make the XHR to get the data, then update the card 68 | return fetch(url).then((response) => { 69 | if (response.status === 200) { 70 | return response.json(); 71 | } 72 | }).then((data) => { 73 | app.hasRequestPending = false; 74 | return data; 75 | }); 76 | }; 77 | 78 | /***************************************************************************** 79 | * 80 | * Start the app 81 | * 82 | ****************************************************************************/ 83 | 84 | app.selectedCityKeys = [ 85 | 'austin', 86 | 'baltimore', 87 | 'boston', 88 | 'chicago', 89 | 'dallas', 90 | 'losangeles', 91 | 'newyork', 92 | 'sanfrancisco', 93 | ]; 94 | 95 | app.selectedCityKeys.forEach((cityKey) => { 96 | app.getForecast(cityKey).then((forecast) => { 97 | app.updateForecastCard(cityKey, forecast); 98 | }); 99 | }); 100 | 101 | // Add feature check for Service Workers here 102 | if('serviceWorker' in navigator) { 103 | navigator.serviceWorker 104 | .register('./service-worker.js') 105 | .then(() => console.log('Service Worker Registered')); 106 | } 107 | -------------------------------------------------------------------------------- /step4/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | 55 | }); 56 | 57 | /***************************************************************************** 58 | * 59 | * Methods for dealing with the network 60 | * 61 | ****************************************************************************/ 62 | 63 | // Gets a forecast for a specific city 64 | app.getForecast = function(cityKey) { 65 | var url = '../data/' + cityKey + '.json'; 66 | app.hasRequestPending = true; 67 | // Make the XHR to get the data, then update the card 68 | return fetch(url).then((response) => { 69 | if (response.status === 200) { 70 | return response.json(); 71 | } 72 | }).then((data) => { 73 | app.hasRequestPending = false; 74 | return data; 75 | }); 76 | }; 77 | 78 | /***************************************************************************** 79 | * 80 | * Start the app 81 | * 82 | ****************************************************************************/ 83 | 84 | app.selectedCityKeys = [ 85 | 'austin', 86 | 'baltimore', 87 | 'boston', 88 | 'chicago', 89 | 'dallas', 90 | 'losangeles', 91 | 'newyork', 92 | 'sanfrancisco', 93 | ]; 94 | 95 | app.selectedCityKeys.forEach((cityKey) => { 96 | app.getForecast(cityKey).then((forecast) => { 97 | app.updateForecastCard(cityKey, forecast); 98 | }); 99 | }); 100 | 101 | // Add feature check for Service Workers here 102 | if('serviceWorker' in navigator) { 103 | navigator.serviceWorker 104 | .register('./service-worker.js') 105 | .then(() => console.log('Service Worker Registered')); 106 | } 107 | -------------------------------------------------------------------------------- /step5/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | 55 | }); 56 | 57 | /***************************************************************************** 58 | * 59 | * Methods for dealing with the network 60 | * 61 | ****************************************************************************/ 62 | 63 | // Gets a forecast for a specific city 64 | app.getForecast = function(cityKey) { 65 | var url = '../data/' + cityKey + '.json'; 66 | app.hasRequestPending = true; 67 | // Make the XHR to get the data, then update the card 68 | return fetch(url).then((response) => { 69 | if (response.status === 200) { 70 | return response.json(); 71 | } 72 | }).then((data) => { 73 | app.hasRequestPending = false; 74 | return data; 75 | }); 76 | }; 77 | 78 | /***************************************************************************** 79 | * 80 | * Listen for the add to home screen events 81 | * 82 | ****************************************************************************/ 83 | 84 | window.addEventListener('beforeinstallprompt', function(e) { 85 | console.log('[App] Showing install prompt'); 86 | 87 | // e.userChoice will return a Promise. 88 | // For more details read: http://www.html5rocks.com/en/tutorials/es6/promises/ 89 | e.userChoice.then(function(choiceResult) { 90 | console.log(choiceResult.outcome); 91 | }); 92 | }); 93 | 94 | /***************************************************************************** 95 | * 96 | * Start the app 97 | * 98 | ****************************************************************************/ 99 | 100 | app.selectedCityKeys = [ 101 | 'austin', 102 | 'baltimore', 103 | 'boston', 104 | 'chicago', 105 | 'dallas', 106 | 'losangeles', 107 | 'newyork', 108 | 'sanfrancisco', 109 | ]; 110 | 111 | app.selectedCityKeys.forEach((cityKey) => { 112 | app.getForecast(cityKey).then((forecast) => { 113 | app.updateForecastCard(cityKey, forecast); 114 | }); 115 | }); 116 | 117 | // Add feature check for Service Workers here 118 | if('serviceWorker' in navigator) { 119 | navigator.serviceWorker 120 | .register('./service-worker.js') 121 | .then(() => console.log('Service Worker Registered')); 122 | } 123 | -------------------------------------------------------------------------------- /step6-complete/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /***************************************************************************** 4 | * 5 | * Setup the core app model 6 | * 7 | ****************************************************************************/ 8 | 9 | var app = { 10 | isLoading: true, 11 | hasRequestPending: false, 12 | visibleCards: {}, 13 | selectedCityKeys: [], 14 | spinner: document.querySelector('.loader'), 15 | cardTemplate: document.querySelector('.cardTemplate'), 16 | container: document.querySelector('#container'), 17 | daysOfWeek: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 18 | }; 19 | 20 | /***************************************************************************** 21 | * 22 | * Methods to update/refresh the UI 23 | * 24 | ****************************************************************************/ 25 | 26 | // Updates a weather card with the latest weather forecast. If the card 27 | // doesn't already exist, it's cloned from the template. 28 | app.updateForecastCard = function(cityKey, data) { 29 | var card = app.visibleCards[cityKey]; 30 | if (!card) { 31 | card = app.cardTemplate.cloneNode(true); 32 | card.classList.remove('cardTemplate'); 33 | card.querySelector('.location').textContent = data.city; 34 | card.removeAttribute('hidden'); 35 | app.container.appendChild(card); 36 | app.visibleCards[cityKey] = card; 37 | } 38 | card.querySelector('.temperature').innerHTML = 39 | Math.round(data.currently.temperature) + '°F'; 40 | if (app.isLoading) { 41 | app.spinner.setAttribute('hidden', true); 42 | app.container.removeAttribute('hidden'); 43 | app.isLoading = false; 44 | } 45 | }; 46 | 47 | /***************************************************************************** 48 | * 49 | * Event listeners for the UI 50 | * 51 | ****************************************************************************/ 52 | 53 | document.querySelector('#butNotif').addEventListener('click', (e) => { 54 | // Request permission to send notifications 55 | Notification.requestPermission().then(() => { 56 | // Get a reference to the SW 57 | return navigator.serviceWorker.ready; 58 | }).then((sw) => { 59 | // Tell it to subscribe with the push server 60 | return sw.pushManager.subscribe({userVisibleOnly: true}); 61 | }).then((subscription) => { 62 | // Send details about the subscription to the server 63 | return fetch('../push', { 64 | method: 'POST', 65 | body: JSON.stringify({ 66 | action: 'subscribe', 67 | subscription: subscription 68 | }), 69 | headers: new Headers({ 70 | 'Content-Type': 'application/json' 71 | }) 72 | }); 73 | }); 74 | }); 75 | 76 | /***************************************************************************** 77 | * 78 | * Methods for dealing with the network 79 | * 80 | ****************************************************************************/ 81 | 82 | // Gets a forecast for a specific city 83 | app.getForecast = function(cityKey) { 84 | var url = '../data/' + cityKey + '.json'; 85 | app.hasRequestPending = true; 86 | // Make the XHR to get the data, then update the card 87 | return fetch(url).then((response) => { 88 | if (response.status === 200) { 89 | return response.json(); 90 | } 91 | }).then((data) => { 92 | app.hasRequestPending = false; 93 | return data; 94 | }); 95 | }; 96 | 97 | /***************************************************************************** 98 | * 99 | * Listen for the add to home screen events 100 | * 101 | ****************************************************************************/ 102 | 103 | window.addEventListener('beforeinstallprompt', function(e) { 104 | console.log('[App] Showing install prompt'); 105 | 106 | // e.userChoice will return a Promise. 107 | // For more details read: http://www.html5rocks.com/en/tutorials/es6/promises/ 108 | e.userChoice.then(function(choiceResult) { 109 | console.log(choiceResult.outcome); 110 | }); 111 | }); 112 | 113 | /***************************************************************************** 114 | * 115 | * Start the app 116 | * 117 | ****************************************************************************/ 118 | 119 | app.selectedCityKeys = [ 120 | 'austin', 121 | 'baltimore', 122 | 'boston', 123 | 'chicago', 124 | 'dallas', 125 | 'losangeles', 126 | 'newyork', 127 | 'sanfrancisco', 128 | ]; 129 | 130 | app.selectedCityKeys.forEach((cityKey) => { 131 | app.getForecast(cityKey).then((forecast) => { 132 | app.updateForecastCard(cityKey, forecast); 133 | }); 134 | }); 135 | 136 | // Add feature check for Service Workers here 137 | if('serviceWorker' in navigator) { 138 | navigator.serviceWorker 139 | .register('./service-worker.js') 140 | .then(() => console.log('Service Worker Registered')); 141 | } 142 | -------------------------------------------------------------------------------- /step1/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step2/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step3/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step4/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step5/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step6-complete/styles/inline.css: -------------------------------------------------------------------------------- 1 | .header,body { 2 | display: -webkit-box; 3 | display: -webkit-flex; 4 | display: -ms-flexbox; 5 | -webkit-box-direction: normal 6 | } 7 | 8 | *,.card,.loader #spinner { 9 | box-sizing: border-box 10 | } 11 | 12 | body,html { 13 | padding: 0; 14 | margin: 0; 15 | height: 100%; 16 | width: 100%; 17 | font-family: Helvetica,Verdana,sans-serif; 18 | font-weight: 400; 19 | font-display: optional; 20 | color: #444; 21 | -webkit-font-smoothing: antialiased; 22 | -moz-osx-font-smoothing: grayscale 23 | } 24 | 25 | body { 26 | display: flex; 27 | -webkit-box-orient: vertical; 28 | -webkit-flex-direction: column; 29 | -ms-flex-direction: column; 30 | flex-direction: column; 31 | -webkit-flex-wrap: nowrap; 32 | -ms-flex-wrap: nowrap; 33 | flex-wrap: nowrap; 34 | -webkit-box-pack: start; 35 | -webkit-justify-content: flex-start; 36 | -ms-flex-pack: start; 37 | justify-content: flex-start; 38 | -webkit-box-align: stretch; 39 | -webkit-align-items: stretch; 40 | -ms-flex-align: stretch; 41 | align-items: stretch; 42 | -webkit-align-content: stretch; 43 | -ms-flex-line-pack: stretch; 44 | align-content: stretch; 45 | background: #ececec 46 | } 47 | 48 | .header { 49 | width: 100%; 50 | height: 56px; 51 | color: #FFF; 52 | background: #3F51B5; 53 | position: fixed; 54 | font-size: 20px; 55 | box-shadow: 0 4px 5px 0 rgba(0,0,0,.14),0 2px 9px 1px rgba(0,0,0,.12),0 4px 2px -2px rgba(0,0,0,.2); 56 | padding: 16px 16px 0; 57 | will-change: transform; 58 | display: flex; 59 | flex-direction: row; 60 | flex-wrap: nowrap; 61 | justify-content: flex-start; 62 | align-items: stretch; 63 | align-content: center; 64 | z-index: 10; 65 | } 66 | 67 | .header .headerButton { 68 | width: 24px; 69 | height: 24px; 70 | margin-right: 16px; 71 | text-indent: -30000px; 72 | overflow: hidden; 73 | opacity: .54; 74 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 75 | transition: opacity 333ms cubic-bezier(0,0,.21,1); 76 | border: none; 77 | outline: 0 78 | } 79 | 80 | .card,.dialog { 81 | border-radius: 2px 82 | } 83 | 84 | .header #butNotif { 85 | background: url(../images/ic_notifications_white_24px.svg) center center no-repeat 86 | } 87 | 88 | .header__title { 89 | font-weight: 400; 90 | font-size: 20px; 91 | margin: 0; 92 | -webkit-box-flex: 1; 93 | -webkit-flex: 1; 94 | -ms-flex: 1; 95 | flex: 1 96 | } 97 | 98 | .loader { 99 | left: 50%; 100 | top: 50%; 101 | position: fixed; 102 | -webkit-transform: translate(-50%,-50%); 103 | transform: translate(-50%,-50%) 104 | } 105 | 106 | .loader #spinner { 107 | stroke: #673AB7; 108 | stroke-width: 3px; 109 | -webkit-transform-origin: 50%; 110 | transform-origin: 50%; 111 | -webkit-animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite; 112 | animation: line 1.6s cubic-bezier(.4,0,.2,1) infinite,rotate 1.6s linear infinite 113 | } 114 | 115 | @-webkit-keyframes rotate { 116 | from { 117 | -webkit-transform: rotate(0); 118 | transform: rotate(0) 119 | } 120 | 121 | to { 122 | -webkit-transform: rotate(450deg); 123 | transform: rotate(450deg) 124 | } 125 | } 126 | 127 | @keyframes rotate { 128 | from { 129 | -webkit-transform: rotate(0); 130 | transform: rotate(0) 131 | } 132 | 133 | to { 134 | -webkit-transform: rotate(450deg); 135 | transform: rotate(450deg) 136 | } 137 | } 138 | 139 | @-webkit-keyframes line { 140 | 0% { 141 | stroke-dasharray: 2,85.964; 142 | -webkit-transform: rotate(0); 143 | transform: rotate(0) 144 | } 145 | 146 | 50% { 147 | stroke-dasharray: 65.973,21.9911; 148 | stroke-dashoffset: 0 149 | } 150 | 151 | 100% { 152 | stroke-dasharray: 2,85.964; 153 | stroke-dashoffset: -65.973; 154 | -webkit-transform: rotate(90deg); 155 | transform: rotate(90deg) 156 | } 157 | } 158 | 159 | @keyframes line { 160 | 0% { 161 | stroke-dasharray: 2,85.964; 162 | -webkit-transform: rotate(0); 163 | transform: rotate(0) 164 | } 165 | 166 | 50% { 167 | stroke-dasharray: 65.973,21.9911; 168 | stroke-dashoffset: 0 169 | } 170 | 171 | 100% { 172 | stroke-dasharray: 2,85.964; 173 | stroke-dashoffset: -65.973; 174 | -webkit-transform: rotate(90deg); 175 | transform: rotate(90deg) 176 | } 177 | } 178 | 179 | #container { 180 | padding-top: 60px; 181 | flex: 1; 182 | } 183 | 184 | .dialog-container { 185 | background: rgba(0,0,0,.57); 186 | position: fixed; 187 | left: 0; 188 | top: 0; 189 | width: 100%; 190 | height: 100%; 191 | opacity: 0; 192 | pointer-events: none; 193 | will-change: opacity; 194 | -webkit-transition: opacity 333ms cubic-bezier(0,0,.21,1); 195 | transition: opacity 333ms cubic-bezier(0,0,.21,1) 196 | } 197 | 198 | .dialog-container--visible { 199 | opacity: 1; 200 | pointer-events: auto 201 | } 202 | 203 | .dialog { 204 | background: #FFF; 205 | box-shadow: 0 0 14px rgba(0,0,0,.24),0 14px 28px rgba(0,0,0,.48); 206 | min-width: 280px; 207 | position: absolute; 208 | left: 50%; 209 | top: 50%; 210 | -webkit-transform: translate(-50%,-50%) translateY(30px); 211 | transform: translate(-50%,-50%) translateY(30px); 212 | -webkit-transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 213 | transition: -webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 214 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms; 215 | transition: transform 333ms cubic-bezier(0,0,.21,1) 50ms,-webkit-transform 333ms cubic-bezier(0,0,.21,1) 50ms; 216 | padding: 24px 217 | } 218 | 219 | .card { 220 | padding: 16px; 221 | position: relative; 222 | background: #fff; 223 | margin: 16px; 224 | box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12) 225 | } 226 | 227 | .weather-forecast .location { 228 | font-size: 1.75em 229 | } 230 | 231 | .weather-forecast .date,.weather-forecast .description { 232 | font-size: 1.25em 233 | } 234 | 235 | .weather-forecast .current { 236 | display: -webkit-box; 237 | display: -webkit-flex; 238 | display: -ms-flexbox; 239 | display: flex 240 | } 241 | 242 | .weather-forecast .current .icon { 243 | width: 128px; 244 | height: 128px 245 | } 246 | 247 | .weather-forecast .current .visual { 248 | display: -webkit-box; 249 | display: -webkit-flex; 250 | display: -ms-flexbox; 251 | display: flex; 252 | font-size: 4em 253 | } 254 | 255 | .weather-forecast .current .visual .scale { 256 | font-size: .5em; 257 | vertical-align: super 258 | } 259 | 260 | .weather-forecast .current .description,.weather-forecast .current .visual { 261 | -webkit-box-flex: 1; 262 | -webkit-flex-grow: 1; 263 | -ms-flex-positive: 1; 264 | flex-grow: 1 265 | } 266 | 267 | .weather-forecast .current .feels-like:before { 268 | content: "Feels like: "; 269 | color: #888 270 | } 271 | 272 | .weather-forecast .current .wind:before { 273 | content: "Wind: "; 274 | color: #888 275 | } 276 | 277 | .weather-forecast .current .precip:before { 278 | content: "Precipitation: "; 279 | color: #888 280 | } 281 | 282 | .weather-forecast .current .humidity:before { 283 | content: "Humidity: "; 284 | color: #888 285 | } 286 | 287 | .weather-forecast .current .pollen:before { 288 | content: "Pollen Count: "; 289 | color: #888 290 | } 291 | 292 | .weather-forecast .current .pcount:before { 293 | content: "Pollen "; 294 | color: #888 295 | } 296 | 297 | .weather-forecast .future { 298 | display: -webkit-box; 299 | display: -webkit-flex; 300 | display: -ms-flexbox; 301 | display: flex 302 | } 303 | 304 | .weather-forecast .future .oneday { 305 | -webkit-box-flex: 1; 306 | -webkit-flex-grow: 1; 307 | -ms-flex-positive: 1; 308 | flex-grow: 1; 309 | text-align: center 310 | } 311 | 312 | .weather-forecast .future .oneday .icon { 313 | width: 64px; 314 | height: 64px; 315 | margin-left: auto; 316 | margin-right: auto 317 | } 318 | 319 | .weather-forecast .future .oneday .temp-high,.weather-forecast .future .oneday .temp-low { 320 | display: inline-block 321 | } 322 | 323 | .weather-forecast .future .oneday .temp-low { 324 | color: #888 325 | } 326 | 327 | .weather-forecast .icon { 328 | background-repeat: no-repeat; 329 | background-size: contain 330 | } 331 | 332 | .weather-forecast .icon.clear-day,.weather-forecast .icon.clear-night { 333 | background-image: url(../images/clear.png) 334 | } 335 | 336 | .weather-forecast .icon.rain { 337 | background-image: url(../images/rain.png) 338 | } 339 | 340 | .weather-forecast .icon.snow { 341 | background-image: url(../images/snow.png) 342 | } 343 | 344 | .weather-forecast .icon.sleet { 345 | background-image: url(../images/sleet.png) 346 | } 347 | 348 | .weather-forecast .icon.wind { 349 | background-image: url(../images/wind.png) 350 | } 351 | 352 | .weather-forecast .icon.fog { 353 | background-image: url(../images/fog.png) 354 | } 355 | 356 | .weather-forecast .icon.cloudy { 357 | background-image: url(../images/cloudy.png) 358 | } 359 | 360 | .weather-forecast .icon.partly-cloudy-day,.weather-forecast .icon.partly-cloudy-night { 361 | background-image: url(../images/partly-cloudy.png) 362 | } 363 | 364 | .weather-forecast .icon.thunderstorms { 365 | background-image: url(../images/thunderstorms.png) 366 | } 367 | 368 | @media (max-width: 450px) { 369 | .weather-forecast .date,.weather-forecast .description { 370 | font-size:.9em 371 | } 372 | 373 | .weather-forecast .current .icon { 374 | width: 96px; 375 | height: 96px 376 | } 377 | 378 | .weather-forecast .current .visual { 379 | font-size: 3em 380 | } 381 | 382 | .weather-forecast .future .oneday .icon { 383 | width: 32px; 384 | height: 32px 385 | } 386 | } 387 | -------------------------------------------------------------------------------- /step1/scripts/push-client.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2015 Google Inc. All Rights Reserved. 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o