[{"data":1,"prerenderedAt":4892},["ShallowReactive",2],{"article_list_mobile_":3},[4,1550],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"excerpt":10,"tags":11,"publishDate":15,"body":16,"_type":1541,"_id":1542,"_source":1543,"_file":1544,"_stem":1545,"_extension":1546,"author":1547},"/ckeefer/2017-3/morepwatoya-part2","2017-3",false,"","More PWA to Ya! (Progressive Web Apps, Part 2)","Last time, we got into the nitty gritty on how to make your web application into a Progressive Web Application (PWA to it's friends). I promised we'd dig even deeper this time, and show you how to make your web app a little more 'native' on Android - and how to deal with iOS Safari's special snowflake syndrome.",[12,13,14],"mobile","pwa","series","2017-03-01",{"type":17,"children":18,"toc":1535},"root",[19,34,41,56,81,86,523,528,563,574,585,596,607,626,646,659,672,739,744,749,755,760,765,779,801,1402,1416,1445,1458,1464,1469,1483,1506,1512,1524,1529],{"type":20,"tag":21,"props":22,"children":23},"element","p",{},[24,32],{"type":20,"tag":25,"props":26,"children":28},"a",{"href":27},"/search/user:ckeefer/more/pwa",[29],{"type":30,"value":31},"text","Last time",{"type":30,"value":33},", we got into the nitty gritty on how to make your web application into a Progressive Web Application (PWA to it's friends). I promised we'd dig even deeper this time, and show you how to make your web app a little more 'native' on Android - and how to deal with iOS Safari's special snowflake syndrome.",{"type":20,"tag":35,"props":36,"children":38},"h2",{"id":37},"manifestly-so",[39],{"type":30,"value":40},"Manifestly So",{"type":20,"tag":21,"props":42,"children":43},{},[44,46,54],{"type":30,"value":45},"The key to gaining first-class citizen status on Android for our PWA is the ",{"type":20,"tag":25,"props":47,"children":51},{"href":48,"rel":49},"https://developer.mozilla.org/en-US/docs/Web/Manifest",[50],"nofollow",[52],{"type":30,"value":53},"Manifest",{"type":30,"value":55},". This allows us to declare certain details about our PWA, and how we'd like it to be handled by the browser. We won't go into all the possibilities - see the MDN link before for all of the details - but instead look at a few common scenarios, namely:",{"type":20,"tag":57,"props":58,"children":59},"ol",{},[60,66,71,76],{"type":20,"tag":61,"props":62,"children":63},"li",{},[64],{"type":30,"value":65},"You want to specify a certain application name, and 'friendly' short form of that name for display in settings and on the home screen;",{"type":20,"tag":61,"props":67,"children":68},{},[69],{"type":30,"value":70},"Your app can be installed to the Home Screen from the web app site, and will automatically prompt the user to do so;",{"type":20,"tag":61,"props":72,"children":73},{},[74],{"type":30,"value":75},"You want to specify some values for things like background color, theme color, and icons, to control the way the app will look on the home screen, and how it will look when it's loading;",{"type":20,"tag":61,"props":77,"children":78},{},[79],{"type":30,"value":80},"You want to lock the app into a certain display orientation.",{"type":20,"tag":21,"props":82,"children":83},{},[84],{"type":30,"value":85},"To that effect, here's an example Manifest, which we'll be dissecting momentarily:",{"type":20,"tag":87,"props":88,"children":92},"pre",{"className":89,"code":90,"language":91,"meta":8,"style":8},"language-json shiki shiki-themes github-light github-dark","{\n    \"short_name\": \"App Name\",\n    \"name\": \"Long App Name for Settings Or Lock Screen\",\n    \"background_color\": \"#hexcode\",\n    \"theme_color\": \"#hexcode\",\n    \"display\": \"standalone\",\n    \"orientation\": \"portrait\",\n    \"icons\": [\n        {\n            \"src\": \"/static/img/icon180x180.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"180x180\"\n        },\n        {\n            \"src\": \"/static/img/icon192x192.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"192x192\"\n        },\n        {\n            \"src\": \"/static/img/icon512x512.png\",\n            \"type\": \"image/png\",\n            \"sizes\": \"512x512\"\n        }\n    ],\n    \"start_url\": \"/\"\n}\n","json",[93],{"type":20,"tag":94,"props":95,"children":96},"code",{"__ignoreMap":8},[97,109,135,157,179,200,222,244,258,267,289,311,329,338,346,367,387,404,412,420,441,461,478,487,496,514],{"type":20,"tag":98,"props":99,"children":102},"span",{"class":100,"line":101},"line",1,[103],{"type":20,"tag":98,"props":104,"children":106},{"style":105},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[107],{"type":30,"value":108},"{\n",{"type":20,"tag":98,"props":110,"children":112},{"class":100,"line":111},2,[113,119,124,130],{"type":20,"tag":98,"props":114,"children":116},{"style":115},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[117],{"type":30,"value":118},"    \"short_name\"",{"type":20,"tag":98,"props":120,"children":121},{"style":105},[122],{"type":30,"value":123},": ",{"type":20,"tag":98,"props":125,"children":127},{"style":126},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[128],{"type":30,"value":129},"\"App Name\"",{"type":20,"tag":98,"props":131,"children":132},{"style":105},[133],{"type":30,"value":134},",\n",{"type":20,"tag":98,"props":136,"children":138},{"class":100,"line":137},3,[139,144,148,153],{"type":20,"tag":98,"props":140,"children":141},{"style":115},[142],{"type":30,"value":143},"    \"name\"",{"type":20,"tag":98,"props":145,"children":146},{"style":105},[147],{"type":30,"value":123},{"type":20,"tag":98,"props":149,"children":150},{"style":126},[151],{"type":30,"value":152},"\"Long App Name for Settings Or Lock Screen\"",{"type":20,"tag":98,"props":154,"children":155},{"style":105},[156],{"type":30,"value":134},{"type":20,"tag":98,"props":158,"children":160},{"class":100,"line":159},4,[161,166,170,175],{"type":20,"tag":98,"props":162,"children":163},{"style":115},[164],{"type":30,"value":165},"    \"background_color\"",{"type":20,"tag":98,"props":167,"children":168},{"style":105},[169],{"type":30,"value":123},{"type":20,"tag":98,"props":171,"children":172},{"style":126},[173],{"type":30,"value":174},"\"#hexcode\"",{"type":20,"tag":98,"props":176,"children":177},{"style":105},[178],{"type":30,"value":134},{"type":20,"tag":98,"props":180,"children":182},{"class":100,"line":181},5,[183,188,192,196],{"type":20,"tag":98,"props":184,"children":185},{"style":115},[186],{"type":30,"value":187},"    \"theme_color\"",{"type":20,"tag":98,"props":189,"children":190},{"style":105},[191],{"type":30,"value":123},{"type":20,"tag":98,"props":193,"children":194},{"style":126},[195],{"type":30,"value":174},{"type":20,"tag":98,"props":197,"children":198},{"style":105},[199],{"type":30,"value":134},{"type":20,"tag":98,"props":201,"children":203},{"class":100,"line":202},6,[204,209,213,218],{"type":20,"tag":98,"props":205,"children":206},{"style":115},[207],{"type":30,"value":208},"    \"display\"",{"type":20,"tag":98,"props":210,"children":211},{"style":105},[212],{"type":30,"value":123},{"type":20,"tag":98,"props":214,"children":215},{"style":126},[216],{"type":30,"value":217},"\"standalone\"",{"type":20,"tag":98,"props":219,"children":220},{"style":105},[221],{"type":30,"value":134},{"type":20,"tag":98,"props":223,"children":225},{"class":100,"line":224},7,[226,231,235,240],{"type":20,"tag":98,"props":227,"children":228},{"style":115},[229],{"type":30,"value":230},"    \"orientation\"",{"type":20,"tag":98,"props":232,"children":233},{"style":105},[234],{"type":30,"value":123},{"type":20,"tag":98,"props":236,"children":237},{"style":126},[238],{"type":30,"value":239},"\"portrait\"",{"type":20,"tag":98,"props":241,"children":242},{"style":105},[243],{"type":30,"value":134},{"type":20,"tag":98,"props":245,"children":247},{"class":100,"line":246},8,[248,253],{"type":20,"tag":98,"props":249,"children":250},{"style":115},[251],{"type":30,"value":252},"    \"icons\"",{"type":20,"tag":98,"props":254,"children":255},{"style":105},[256],{"type":30,"value":257},": [\n",{"type":20,"tag":98,"props":259,"children":261},{"class":100,"line":260},9,[262],{"type":20,"tag":98,"props":263,"children":264},{"style":105},[265],{"type":30,"value":266},"        {\n",{"type":20,"tag":98,"props":268,"children":270},{"class":100,"line":269},10,[271,276,280,285],{"type":20,"tag":98,"props":272,"children":273},{"style":115},[274],{"type":30,"value":275},"            \"src\"",{"type":20,"tag":98,"props":277,"children":278},{"style":105},[279],{"type":30,"value":123},{"type":20,"tag":98,"props":281,"children":282},{"style":126},[283],{"type":30,"value":284},"\"/static/img/icon180x180.png\"",{"type":20,"tag":98,"props":286,"children":287},{"style":105},[288],{"type":30,"value":134},{"type":20,"tag":98,"props":290,"children":292},{"class":100,"line":291},11,[293,298,302,307],{"type":20,"tag":98,"props":294,"children":295},{"style":115},[296],{"type":30,"value":297},"            \"type\"",{"type":20,"tag":98,"props":299,"children":300},{"style":105},[301],{"type":30,"value":123},{"type":20,"tag":98,"props":303,"children":304},{"style":126},[305],{"type":30,"value":306},"\"image/png\"",{"type":20,"tag":98,"props":308,"children":309},{"style":105},[310],{"type":30,"value":134},{"type":20,"tag":98,"props":312,"children":314},{"class":100,"line":313},12,[315,320,324],{"type":20,"tag":98,"props":316,"children":317},{"style":115},[318],{"type":30,"value":319},"            \"sizes\"",{"type":20,"tag":98,"props":321,"children":322},{"style":105},[323],{"type":30,"value":123},{"type":20,"tag":98,"props":325,"children":326},{"style":126},[327],{"type":30,"value":328},"\"180x180\"\n",{"type":20,"tag":98,"props":330,"children":332},{"class":100,"line":331},13,[333],{"type":20,"tag":98,"props":334,"children":335},{"style":105},[336],{"type":30,"value":337},"        },\n",{"type":20,"tag":98,"props":339,"children":341},{"class":100,"line":340},14,[342],{"type":20,"tag":98,"props":343,"children":344},{"style":105},[345],{"type":30,"value":266},{"type":20,"tag":98,"props":347,"children":349},{"class":100,"line":348},15,[350,354,358,363],{"type":20,"tag":98,"props":351,"children":352},{"style":115},[353],{"type":30,"value":275},{"type":20,"tag":98,"props":355,"children":356},{"style":105},[357],{"type":30,"value":123},{"type":20,"tag":98,"props":359,"children":360},{"style":126},[361],{"type":30,"value":362},"\"/static/img/icon192x192.png\"",{"type":20,"tag":98,"props":364,"children":365},{"style":105},[366],{"type":30,"value":134},{"type":20,"tag":98,"props":368,"children":370},{"class":100,"line":369},16,[371,375,379,383],{"type":20,"tag":98,"props":372,"children":373},{"style":115},[374],{"type":30,"value":297},{"type":20,"tag":98,"props":376,"children":377},{"style":105},[378],{"type":30,"value":123},{"type":20,"tag":98,"props":380,"children":381},{"style":126},[382],{"type":30,"value":306},{"type":20,"tag":98,"props":384,"children":385},{"style":105},[386],{"type":30,"value":134},{"type":20,"tag":98,"props":388,"children":390},{"class":100,"line":389},17,[391,395,399],{"type":20,"tag":98,"props":392,"children":393},{"style":115},[394],{"type":30,"value":319},{"type":20,"tag":98,"props":396,"children":397},{"style":105},[398],{"type":30,"value":123},{"type":20,"tag":98,"props":400,"children":401},{"style":126},[402],{"type":30,"value":403},"\"192x192\"\n",{"type":20,"tag":98,"props":405,"children":407},{"class":100,"line":406},18,[408],{"type":20,"tag":98,"props":409,"children":410},{"style":105},[411],{"type":30,"value":337},{"type":20,"tag":98,"props":413,"children":415},{"class":100,"line":414},19,[416],{"type":20,"tag":98,"props":417,"children":418},{"style":105},[419],{"type":30,"value":266},{"type":20,"tag":98,"props":421,"children":423},{"class":100,"line":422},20,[424,428,432,437],{"type":20,"tag":98,"props":425,"children":426},{"style":115},[427],{"type":30,"value":275},{"type":20,"tag":98,"props":429,"children":430},{"style":105},[431],{"type":30,"value":123},{"type":20,"tag":98,"props":433,"children":434},{"style":126},[435],{"type":30,"value":436},"\"/static/img/icon512x512.png\"",{"type":20,"tag":98,"props":438,"children":439},{"style":105},[440],{"type":30,"value":134},{"type":20,"tag":98,"props":442,"children":444},{"class":100,"line":443},21,[445,449,453,457],{"type":20,"tag":98,"props":446,"children":447},{"style":115},[448],{"type":30,"value":297},{"type":20,"tag":98,"props":450,"children":451},{"style":105},[452],{"type":30,"value":123},{"type":20,"tag":98,"props":454,"children":455},{"style":126},[456],{"type":30,"value":306},{"type":20,"tag":98,"props":458,"children":459},{"style":105},[460],{"type":30,"value":134},{"type":20,"tag":98,"props":462,"children":464},{"class":100,"line":463},22,[465,469,473],{"type":20,"tag":98,"props":466,"children":467},{"style":115},[468],{"type":30,"value":319},{"type":20,"tag":98,"props":470,"children":471},{"style":105},[472],{"type":30,"value":123},{"type":20,"tag":98,"props":474,"children":475},{"style":126},[476],{"type":30,"value":477},"\"512x512\"\n",{"type":20,"tag":98,"props":479,"children":481},{"class":100,"line":480},23,[482],{"type":20,"tag":98,"props":483,"children":484},{"style":105},[485],{"type":30,"value":486},"        }\n",{"type":20,"tag":98,"props":488,"children":490},{"class":100,"line":489},24,[491],{"type":20,"tag":98,"props":492,"children":493},{"style":105},[494],{"type":30,"value":495},"    ],\n",{"type":20,"tag":98,"props":497,"children":499},{"class":100,"line":498},25,[500,505,509],{"type":20,"tag":98,"props":501,"children":502},{"style":115},[503],{"type":30,"value":504},"    \"start_url\"",{"type":20,"tag":98,"props":506,"children":507},{"style":105},[508],{"type":30,"value":123},{"type":20,"tag":98,"props":510,"children":511},{"style":126},[512],{"type":30,"value":513},"\"/\"\n",{"type":20,"tag":98,"props":515,"children":517},{"class":100,"line":516},26,[518],{"type":20,"tag":98,"props":519,"children":520},{"style":105},[521],{"type":30,"value":522},"}\n",{"type":20,"tag":21,"props":524,"children":525},{},[526],{"type":30,"value":527},"So, let's go through this line-by-line.",{"type":20,"tag":21,"props":529,"children":530},{},[531,533,539,541,547,549,554,556,561],{"type":30,"value":532},"The ",{"type":20,"tag":94,"props":534,"children":536},{"className":535},[],[537],{"type":30,"value":538},"short_name",{"type":30,"value":540}," and ",{"type":20,"tag":94,"props":542,"children":544},{"className":543},[],[545],{"type":30,"value":546},"name",{"type":30,"value":548}," properties are fairly self-explanatory. The ",{"type":20,"tag":94,"props":550,"children":552},{"className":551},[],[553],{"type":30,"value":538},{"type":30,"value":555}," will be displayed anywhere the OS decides the ",{"type":20,"tag":94,"props":557,"children":559},{"className":558},[],[560],{"type":30,"value":546},{"type":30,"value":562}," won't fit. These could, of course, simply be the same string. How the OS deals with short_names that are still too long is up to the OS, but you can expect truncation of your name, so try and keep the short_name pithy.",{"type":20,"tag":21,"props":564,"children":565},{},[566,572],{"type":20,"tag":94,"props":567,"children":569},{"className":568},[],[570],{"type":30,"value":571},"background_color",{"type":30,"value":573}," is an important one, as this is going to get used by the OS to generate the 'splash screen' that the user sees when they load your application. If you specify icons, the background-color will be displayed behind the chosen icon.",{"type":20,"tag":21,"props":575,"children":576},{},[577,583],{"type":20,"tag":94,"props":578,"children":580},{"className":579},[],[581],{"type":30,"value":582},"theme_color",{"type":30,"value":584}," is a little more ambiguous, as what's done with it is up to the OS, and might change; right now, specifying it will determine what colour surrounds the application and is used for it in the task switcher.",{"type":20,"tag":21,"props":586,"children":587},{},[588,594],{"type":20,"tag":94,"props":589,"children":591},{"className":590},[],[592],{"type":30,"value":593},"display",{"type":30,"value":595}," is another big one - it determines how 'app-like' your PWA is going to appear. If you specify 'standalone', as we have here, it should look and feel like a standalone application - the browser chrome should be excluded, but items like the status bar can be displayed. This is suitable for a LoB-type application. There's also 'fullscreen', which gives you every last pixel - perfect for games - 'minimal-ui', which incorporates some of the browser chrome (how much depends on the browser), and 'browser', which leaves all of the browser chrome around your PWA intact, as though it were being viewed in the browser.",{"type":20,"tag":21,"props":597,"children":598},{},[599,605],{"type":20,"tag":94,"props":600,"children":602},{"className":601},[],[603],{"type":30,"value":604},"orientation",{"type":30,"value":606}," is another important one for an app - it allows you to specify that the application should be locked to 'portrait' or 'landscape' orientation, amongst a host of other possible values that you can see detailed in the MDN doc linked above.",{"type":20,"tag":21,"props":608,"children":609},{},[610,616,618,624],{"type":20,"tag":94,"props":611,"children":613},{"className":612},[],[614],{"type":30,"value":615},"icons",{"type":30,"value":617}," is, as mentioned, the icons that will be used for display on the home screen and when generating the 'splash screen' the user will see when loading the application. You should specify an array of options to handle different screen sizes and the different contexts in which an icon will be used (e.g. task switcher, home screen, splash screen, etc.). Relative URLs are resolved relative to the location of the manifest.json file (more on that in a minute); you'll notice we're using all PNG files for our icons, but they don't have to be... unless you're also planning to use them with iOS (and more on ",{"type":20,"tag":619,"props":620,"children":621},"em",{},[622],{"type":30,"value":623},"that",{"type":30,"value":625}," in a minute as well).",{"type":20,"tag":21,"props":627,"children":628},{},[629,631,637,639,644],{"type":30,"value":630},"Finally, ",{"type":20,"tag":94,"props":632,"children":634},{"className":633},[],[635],{"type":30,"value":636},"start_url",{"type":30,"value":638}," is where we want the PWA to be when it loads from the home screen (as it's possible that the user could have chosen to add the PWA to the home screen from any url on your web app, and without this ",{"type":20,"tag":94,"props":640,"children":642},{"className":641},[],[643],{"type":30,"value":636},{"type":30,"value":645}," specified, that would become the start_url for your PWA).",{"type":20,"tag":21,"props":647,"children":648},{},[649,651,657],{"type":30,"value":650},"All of these details should live in a file called ",{"type":20,"tag":94,"props":652,"children":654},{"className":653},[],[655],{"type":30,"value":656},"manifest.json",{"type":30,"value":658}," and should be somewhere your web server can serve static files from, as you'll be linking to it in the head of your base html file.",{"type":20,"tag":21,"props":660,"children":661},{},[662,664,670],{"type":30,"value":663},"Somewhere in the ",{"type":20,"tag":94,"props":665,"children":667},{"className":666},[],[668],{"type":30,"value":669},"\u003Chead>\u003C/head>",{"type":30,"value":671}," of your html index, you should add the following meta tag:",{"type":20,"tag":87,"props":673,"children":677},{"className":674,"code":675,"language":676,"meta":8,"style":8},"language-html shiki shiki-themes github-light github-dark","\u003C!-- PWA Manifest -->\n\u003Clink rel=\"manifest\" href=\"/path/to/manifest.json\">\n","html",[678],{"type":20,"tag":94,"props":679,"children":680},{"__ignoreMap":8},[681,690],{"type":20,"tag":98,"props":682,"children":683},{"class":100,"line":101},[684],{"type":20,"tag":98,"props":685,"children":687},{"style":686},"--shiki-default:#6A737D;--shiki-dark:#6A737D",[688],{"type":30,"value":689},"\u003C!-- PWA Manifest -->\n",{"type":20,"tag":98,"props":691,"children":692},{"class":100,"line":111},[693,698,704,710,715,720,725,729,734],{"type":20,"tag":98,"props":694,"children":695},{"style":105},[696],{"type":30,"value":697},"\u003C",{"type":20,"tag":98,"props":699,"children":701},{"style":700},"--shiki-default:#22863A;--shiki-dark:#85E89D",[702],{"type":30,"value":703},"link",{"type":20,"tag":98,"props":705,"children":707},{"style":706},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[708],{"type":30,"value":709}," rel",{"type":20,"tag":98,"props":711,"children":712},{"style":105},[713],{"type":30,"value":714},"=",{"type":20,"tag":98,"props":716,"children":717},{"style":126},[718],{"type":30,"value":719},"\"manifest\"",{"type":20,"tag":98,"props":721,"children":722},{"style":706},[723],{"type":30,"value":724}," href",{"type":20,"tag":98,"props":726,"children":727},{"style":105},[728],{"type":30,"value":714},{"type":20,"tag":98,"props":730,"children":731},{"style":126},[732],{"type":30,"value":733},"\"/path/to/manifest.json\"",{"type":20,"tag":98,"props":735,"children":736},{"style":105},[737],{"type":30,"value":738},">\n",{"type":20,"tag":21,"props":740,"children":741},{},[742],{"type":30,"value":743},"That's it for Android, you're ready to go!",{"type":20,"tag":21,"props":745,"children":746},{},[747],{"type":30,"value":748},"Now, as long as you're adding meta tags, let's look at the slew of meta tags needed for PWA support on iOS.",{"type":20,"tag":35,"props":750,"children":752},{"id":751},"snowflakes-arent-that-special",[753],{"type":30,"value":754},"Snowflakes aren't that Special",{"type":20,"tag":21,"props":756,"children":757},{},[758],{"type":30,"value":759},"So, by now you're probably pretty excited - you've basically got this PWA thing working, right? Well, sure - unless you need to support iOS.",{"type":20,"tag":21,"props":761,"children":762},{},[763],{"type":30,"value":764},"Because iOS doesn't support Service Worker. Which means it doesn't support PWAs.",{"type":20,"tag":21,"props":766,"children":767},{},[768,770,777],{"type":30,"value":769},"But wait! No need for that wail of despair (or that sixth whiskey) - I haven't lead you down here just to dash all your hopes. ",{"type":20,"tag":25,"props":771,"children":774},{"href":772,"rel":773},"https://webkit.org/status/#specification-service-workers",[50],[775],{"type":30,"value":776},"iOS doesn't support Service Worker yet, but they're working on it",{"type":30,"value":778},". Whether they'll also support the manifest functionality is uncertain, but Service Workers are really the beating heart of PWAs, so one day soon, all the work you've put in so far will pay off on iOS.",{"type":20,"tag":21,"props":780,"children":781},{},[782,784,791,793,799],{"type":30,"value":783},"In the meantime, even though we can have a full-fat PWA, we can get pretty close with iOS's ",{"type":20,"tag":25,"props":785,"children":788},{"href":786,"rel":787},"https://developer.apple.com/library/content/documentation/AppleApplications/Reference/SafariWebContent/ConfiguringWebApplications/ConfiguringWebApplications.html",[50],[789],{"type":30,"value":790},"Web Clips",{"type":30,"value":792}," functionality. That link will give you the details, but let's look at an example. All of the below will go into the ",{"type":20,"tag":94,"props":794,"children":796},{"className":795},[],[797],{"type":30,"value":798},"\u003Chead \\>",{"type":30,"value":800}," of your html document.",{"type":20,"tag":87,"props":802,"children":804},{"className":674,"code":803,"language":676,"meta":8,"style":8},"\u003C!-- iOS specific headers for 'web app' behaviours and look-and-feel. -->\n\u003Cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />\n\u003Cmeta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n\u003Cmeta name=\"apple-mobile-web-app-status-bar-style\" content=\"black-translucent\">\n\u003Cmeta name=\"apple-mobile-web-app-title\" content=\"Your app name\">\n\n\u003C!-- iOS Home Screen Icon -->\n\u003Clink rel=\"apple-touch-icon\" href=\"/static/img/yourAppIcon.png\">\n\n\u003C!-- Splash screens for iOS - these did not work in iOS 9/10, but are now working\n    in iOS 11+ - see https://forums.developer.apple.com/thread/23924. -->\n\n\u003C!-- iPhone 7 Plus & 6/s Plus portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen1242x2208.png' %}\"\n        media=\"(device-width: 414px) and (device-height: 736px)\n                and (-webkit-device-pixel-ratio: 3)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 7 & 6s portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen750x1334.png' %}\"\n        media=\"(device-width: 375px) and (device-height: 667px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 6 portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen750x1294.png' %}\"\n        media=\"(device-width: 375px) and (device-height: 667px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n\n\u003C!-- iPhone 5, SE portrait startup image -->\n\u003Clink href=\"{% static 'img/launchScreen640x1136.png' %}\"\n        media=\"(device-width: 320px) and (device-height: 568px)\n                and (-webkit-device-pixel-ratio: 2)\"\n        rel=\"apple-touch-startup-image\">\n",[805],{"type":20,"tag":94,"props":806,"children":807},{"__ignoreMap":8},[808,816,861,902,943,984,993,1001,1042,1049,1057,1065,1072,1080,1104,1121,1129,1150,1157,1165,1189,1205,1213,1232,1239,1247,1271,1287,1295,1315,1323,1332,1357,1374,1382],{"type":20,"tag":98,"props":809,"children":810},{"class":100,"line":101},[811],{"type":20,"tag":98,"props":812,"children":813},{"style":686},[814],{"type":30,"value":815},"\u003C!-- iOS specific headers for 'web app' behaviours and look-and-feel. -->\n",{"type":20,"tag":98,"props":817,"children":818},{"class":100,"line":111},[819,823,828,833,837,842,847,851,856],{"type":20,"tag":98,"props":820,"children":821},{"style":105},[822],{"type":30,"value":697},{"type":20,"tag":98,"props":824,"children":825},{"style":700},[826],{"type":30,"value":827},"meta",{"type":20,"tag":98,"props":829,"children":830},{"style":706},[831],{"type":30,"value":832}," name",{"type":20,"tag":98,"props":834,"children":835},{"style":105},[836],{"type":30,"value":714},{"type":20,"tag":98,"props":838,"children":839},{"style":126},[840],{"type":30,"value":841},"\"viewport\"",{"type":20,"tag":98,"props":843,"children":844},{"style":706},[845],{"type":30,"value":846}," content",{"type":20,"tag":98,"props":848,"children":849},{"style":105},[850],{"type":30,"value":714},{"type":20,"tag":98,"props":852,"children":853},{"style":126},[854],{"type":30,"value":855},"\"width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no\"",{"type":20,"tag":98,"props":857,"children":858},{"style":105},[859],{"type":30,"value":860}," />\n",{"type":20,"tag":98,"props":862,"children":863},{"class":100,"line":137},[864,868,872,876,880,885,889,893,898],{"type":20,"tag":98,"props":865,"children":866},{"style":105},[867],{"type":30,"value":697},{"type":20,"tag":98,"props":869,"children":870},{"style":700},[871],{"type":30,"value":827},{"type":20,"tag":98,"props":873,"children":874},{"style":706},[875],{"type":30,"value":832},{"type":20,"tag":98,"props":877,"children":878},{"style":105},[879],{"type":30,"value":714},{"type":20,"tag":98,"props":881,"children":882},{"style":126},[883],{"type":30,"value":884},"\"apple-mobile-web-app-capable\"",{"type":20,"tag":98,"props":886,"children":887},{"style":706},[888],{"type":30,"value":846},{"type":20,"tag":98,"props":890,"children":891},{"style":105},[892],{"type":30,"value":714},{"type":20,"tag":98,"props":894,"children":895},{"style":126},[896],{"type":30,"value":897},"\"yes\"",{"type":20,"tag":98,"props":899,"children":900},{"style":105},[901],{"type":30,"value":738},{"type":20,"tag":98,"props":903,"children":904},{"class":100,"line":159},[905,909,913,917,921,926,930,934,939],{"type":20,"tag":98,"props":906,"children":907},{"style":105},[908],{"type":30,"value":697},{"type":20,"tag":98,"props":910,"children":911},{"style":700},[912],{"type":30,"value":827},{"type":20,"tag":98,"props":914,"children":915},{"style":706},[916],{"type":30,"value":832},{"type":20,"tag":98,"props":918,"children":919},{"style":105},[920],{"type":30,"value":714},{"type":20,"tag":98,"props":922,"children":923},{"style":126},[924],{"type":30,"value":925},"\"apple-mobile-web-app-status-bar-style\"",{"type":20,"tag":98,"props":927,"children":928},{"style":706},[929],{"type":30,"value":846},{"type":20,"tag":98,"props":931,"children":932},{"style":105},[933],{"type":30,"value":714},{"type":20,"tag":98,"props":935,"children":936},{"style":126},[937],{"type":30,"value":938},"\"black-translucent\"",{"type":20,"tag":98,"props":940,"children":941},{"style":105},[942],{"type":30,"value":738},{"type":20,"tag":98,"props":944,"children":945},{"class":100,"line":181},[946,950,954,958,962,967,971,975,980],{"type":20,"tag":98,"props":947,"children":948},{"style":105},[949],{"type":30,"value":697},{"type":20,"tag":98,"props":951,"children":952},{"style":700},[953],{"type":30,"value":827},{"type":20,"tag":98,"props":955,"children":956},{"style":706},[957],{"type":30,"value":832},{"type":20,"tag":98,"props":959,"children":960},{"style":105},[961],{"type":30,"value":714},{"type":20,"tag":98,"props":963,"children":964},{"style":126},[965],{"type":30,"value":966},"\"apple-mobile-web-app-title\"",{"type":20,"tag":98,"props":968,"children":969},{"style":706},[970],{"type":30,"value":846},{"type":20,"tag":98,"props":972,"children":973},{"style":105},[974],{"type":30,"value":714},{"type":20,"tag":98,"props":976,"children":977},{"style":126},[978],{"type":30,"value":979},"\"Your app name\"",{"type":20,"tag":98,"props":981,"children":982},{"style":105},[983],{"type":30,"value":738},{"type":20,"tag":98,"props":985,"children":986},{"class":100,"line":202},[987],{"type":20,"tag":98,"props":988,"children":990},{"emptyLinePlaceholder":989},true,[991],{"type":30,"value":992},"\n",{"type":20,"tag":98,"props":994,"children":995},{"class":100,"line":224},[996],{"type":20,"tag":98,"props":997,"children":998},{"style":686},[999],{"type":30,"value":1000},"\u003C!-- iOS Home Screen Icon -->\n",{"type":20,"tag":98,"props":1002,"children":1003},{"class":100,"line":246},[1004,1008,1012,1016,1020,1025,1029,1033,1038],{"type":20,"tag":98,"props":1005,"children":1006},{"style":105},[1007],{"type":30,"value":697},{"type":20,"tag":98,"props":1009,"children":1010},{"style":700},[1011],{"type":30,"value":703},{"type":20,"tag":98,"props":1013,"children":1014},{"style":706},[1015],{"type":30,"value":709},{"type":20,"tag":98,"props":1017,"children":1018},{"style":105},[1019],{"type":30,"value":714},{"type":20,"tag":98,"props":1021,"children":1022},{"style":126},[1023],{"type":30,"value":1024},"\"apple-touch-icon\"",{"type":20,"tag":98,"props":1026,"children":1027},{"style":706},[1028],{"type":30,"value":724},{"type":20,"tag":98,"props":1030,"children":1031},{"style":105},[1032],{"type":30,"value":714},{"type":20,"tag":98,"props":1034,"children":1035},{"style":126},[1036],{"type":30,"value":1037},"\"/static/img/yourAppIcon.png\"",{"type":20,"tag":98,"props":1039,"children":1040},{"style":105},[1041],{"type":30,"value":738},{"type":20,"tag":98,"props":1043,"children":1044},{"class":100,"line":260},[1045],{"type":20,"tag":98,"props":1046,"children":1047},{"emptyLinePlaceholder":989},[1048],{"type":30,"value":992},{"type":20,"tag":98,"props":1050,"children":1051},{"class":100,"line":269},[1052],{"type":20,"tag":98,"props":1053,"children":1054},{"style":686},[1055],{"type":30,"value":1056},"\u003C!-- Splash screens for iOS - these did not work in iOS 9/10, but are now working\n",{"type":20,"tag":98,"props":1058,"children":1059},{"class":100,"line":291},[1060],{"type":20,"tag":98,"props":1061,"children":1062},{"style":686},[1063],{"type":30,"value":1064},"    in iOS 11+ - see https://forums.developer.apple.com/thread/23924. -->\n",{"type":20,"tag":98,"props":1066,"children":1067},{"class":100,"line":313},[1068],{"type":20,"tag":98,"props":1069,"children":1070},{"emptyLinePlaceholder":989},[1071],{"type":30,"value":992},{"type":20,"tag":98,"props":1073,"children":1074},{"class":100,"line":331},[1075],{"type":20,"tag":98,"props":1076,"children":1077},{"style":686},[1078],{"type":30,"value":1079},"\u003C!-- iPhone 7 Plus & 6/s Plus portrait startup image -->\n",{"type":20,"tag":98,"props":1081,"children":1082},{"class":100,"line":340},[1083,1087,1091,1095,1099],{"type":20,"tag":98,"props":1084,"children":1085},{"style":105},[1086],{"type":30,"value":697},{"type":20,"tag":98,"props":1088,"children":1089},{"style":700},[1090],{"type":30,"value":703},{"type":20,"tag":98,"props":1092,"children":1093},{"style":706},[1094],{"type":30,"value":724},{"type":20,"tag":98,"props":1096,"children":1097},{"style":105},[1098],{"type":30,"value":714},{"type":20,"tag":98,"props":1100,"children":1101},{"style":126},[1102],{"type":30,"value":1103},"\"{% static 'img/launchScreen1242x2208.png' %}\"\n",{"type":20,"tag":98,"props":1105,"children":1106},{"class":100,"line":348},[1107,1112,1116],{"type":20,"tag":98,"props":1108,"children":1109},{"style":706},[1110],{"type":30,"value":1111},"        media",{"type":20,"tag":98,"props":1113,"children":1114},{"style":105},[1115],{"type":30,"value":714},{"type":20,"tag":98,"props":1117,"children":1118},{"style":126},[1119],{"type":30,"value":1120},"\"(device-width: 414px) and (device-height: 736px)\n",{"type":20,"tag":98,"props":1122,"children":1123},{"class":100,"line":369},[1124],{"type":20,"tag":98,"props":1125,"children":1126},{"style":126},[1127],{"type":30,"value":1128},"                and (-webkit-device-pixel-ratio: 3)\"\n",{"type":20,"tag":98,"props":1130,"children":1131},{"class":100,"line":389},[1132,1137,1141,1146],{"type":20,"tag":98,"props":1133,"children":1134},{"style":706},[1135],{"type":30,"value":1136},"        rel",{"type":20,"tag":98,"props":1138,"children":1139},{"style":105},[1140],{"type":30,"value":714},{"type":20,"tag":98,"props":1142,"children":1143},{"style":126},[1144],{"type":30,"value":1145},"\"apple-touch-startup-image\"",{"type":20,"tag":98,"props":1147,"children":1148},{"style":105},[1149],{"type":30,"value":738},{"type":20,"tag":98,"props":1151,"children":1152},{"class":100,"line":406},[1153],{"type":20,"tag":98,"props":1154,"children":1155},{"emptyLinePlaceholder":989},[1156],{"type":30,"value":992},{"type":20,"tag":98,"props":1158,"children":1159},{"class":100,"line":414},[1160],{"type":20,"tag":98,"props":1161,"children":1162},{"style":686},[1163],{"type":30,"value":1164},"\u003C!-- iPhone 7 & 6s portrait startup image -->\n",{"type":20,"tag":98,"props":1166,"children":1167},{"class":100,"line":422},[1168,1172,1176,1180,1184],{"type":20,"tag":98,"props":1169,"children":1170},{"style":105},[1171],{"type":30,"value":697},{"type":20,"tag":98,"props":1173,"children":1174},{"style":700},[1175],{"type":30,"value":703},{"type":20,"tag":98,"props":1177,"children":1178},{"style":706},[1179],{"type":30,"value":724},{"type":20,"tag":98,"props":1181,"children":1182},{"style":105},[1183],{"type":30,"value":714},{"type":20,"tag":98,"props":1185,"children":1186},{"style":126},[1187],{"type":30,"value":1188},"\"{% static 'img/launchScreen750x1334.png' %}\"\n",{"type":20,"tag":98,"props":1190,"children":1191},{"class":100,"line":443},[1192,1196,1200],{"type":20,"tag":98,"props":1193,"children":1194},{"style":706},[1195],{"type":30,"value":1111},{"type":20,"tag":98,"props":1197,"children":1198},{"style":105},[1199],{"type":30,"value":714},{"type":20,"tag":98,"props":1201,"children":1202},{"style":126},[1203],{"type":30,"value":1204},"\"(device-width: 375px) and (device-height: 667px)\n",{"type":20,"tag":98,"props":1206,"children":1207},{"class":100,"line":463},[1208],{"type":20,"tag":98,"props":1209,"children":1210},{"style":126},[1211],{"type":30,"value":1212},"                and (-webkit-device-pixel-ratio: 2)\"\n",{"type":20,"tag":98,"props":1214,"children":1215},{"class":100,"line":480},[1216,1220,1224,1228],{"type":20,"tag":98,"props":1217,"children":1218},{"style":706},[1219],{"type":30,"value":1136},{"type":20,"tag":98,"props":1221,"children":1222},{"style":105},[1223],{"type":30,"value":714},{"type":20,"tag":98,"props":1225,"children":1226},{"style":126},[1227],{"type":30,"value":1145},{"type":20,"tag":98,"props":1229,"children":1230},{"style":105},[1231],{"type":30,"value":738},{"type":20,"tag":98,"props":1233,"children":1234},{"class":100,"line":489},[1235],{"type":20,"tag":98,"props":1236,"children":1237},{"emptyLinePlaceholder":989},[1238],{"type":30,"value":992},{"type":20,"tag":98,"props":1240,"children":1241},{"class":100,"line":498},[1242],{"type":20,"tag":98,"props":1243,"children":1244},{"style":686},[1245],{"type":30,"value":1246},"\u003C!-- iPhone 6 portrait startup image -->\n",{"type":20,"tag":98,"props":1248,"children":1249},{"class":100,"line":516},[1250,1254,1258,1262,1266],{"type":20,"tag":98,"props":1251,"children":1252},{"style":105},[1253],{"type":30,"value":697},{"type":20,"tag":98,"props":1255,"children":1256},{"style":700},[1257],{"type":30,"value":703},{"type":20,"tag":98,"props":1259,"children":1260},{"style":706},[1261],{"type":30,"value":724},{"type":20,"tag":98,"props":1263,"children":1264},{"style":105},[1265],{"type":30,"value":714},{"type":20,"tag":98,"props":1267,"children":1268},{"style":126},[1269],{"type":30,"value":1270},"\"{% static 'img/launchScreen750x1294.png' %}\"\n",{"type":20,"tag":98,"props":1272,"children":1274},{"class":100,"line":1273},27,[1275,1279,1283],{"type":20,"tag":98,"props":1276,"children":1277},{"style":706},[1278],{"type":30,"value":1111},{"type":20,"tag":98,"props":1280,"children":1281},{"style":105},[1282],{"type":30,"value":714},{"type":20,"tag":98,"props":1284,"children":1285},{"style":126},[1286],{"type":30,"value":1204},{"type":20,"tag":98,"props":1288,"children":1290},{"class":100,"line":1289},28,[1291],{"type":20,"tag":98,"props":1292,"children":1293},{"style":126},[1294],{"type":30,"value":1212},{"type":20,"tag":98,"props":1296,"children":1298},{"class":100,"line":1297},29,[1299,1303,1307,1311],{"type":20,"tag":98,"props":1300,"children":1301},{"style":706},[1302],{"type":30,"value":1136},{"type":20,"tag":98,"props":1304,"children":1305},{"style":105},[1306],{"type":30,"value":714},{"type":20,"tag":98,"props":1308,"children":1309},{"style":126},[1310],{"type":30,"value":1145},{"type":20,"tag":98,"props":1312,"children":1313},{"style":105},[1314],{"type":30,"value":738},{"type":20,"tag":98,"props":1316,"children":1318},{"class":100,"line":1317},30,[1319],{"type":20,"tag":98,"props":1320,"children":1321},{"emptyLinePlaceholder":989},[1322],{"type":30,"value":992},{"type":20,"tag":98,"props":1324,"children":1326},{"class":100,"line":1325},31,[1327],{"type":20,"tag":98,"props":1328,"children":1329},{"style":686},[1330],{"type":30,"value":1331},"\u003C!-- iPhone 5, SE portrait startup image -->\n",{"type":20,"tag":98,"props":1333,"children":1335},{"class":100,"line":1334},32,[1336,1340,1344,1348,1352],{"type":20,"tag":98,"props":1337,"children":1338},{"style":105},[1339],{"type":30,"value":697},{"type":20,"tag":98,"props":1341,"children":1342},{"style":700},[1343],{"type":30,"value":703},{"type":20,"tag":98,"props":1345,"children":1346},{"style":706},[1347],{"type":30,"value":724},{"type":20,"tag":98,"props":1349,"children":1350},{"style":105},[1351],{"type":30,"value":714},{"type":20,"tag":98,"props":1353,"children":1354},{"style":126},[1355],{"type":30,"value":1356},"\"{% static 'img/launchScreen640x1136.png' %}\"\n",{"type":20,"tag":98,"props":1358,"children":1360},{"class":100,"line":1359},33,[1361,1365,1369],{"type":20,"tag":98,"props":1362,"children":1363},{"style":706},[1364],{"type":30,"value":1111},{"type":20,"tag":98,"props":1366,"children":1367},{"style":105},[1368],{"type":30,"value":714},{"type":20,"tag":98,"props":1370,"children":1371},{"style":126},[1372],{"type":30,"value":1373},"\"(device-width: 320px) and (device-height: 568px)\n",{"type":20,"tag":98,"props":1375,"children":1377},{"class":100,"line":1376},34,[1378],{"type":20,"tag":98,"props":1379,"children":1380},{"style":126},[1381],{"type":30,"value":1212},{"type":20,"tag":98,"props":1383,"children":1385},{"class":100,"line":1384},35,[1386,1390,1394,1398],{"type":20,"tag":98,"props":1387,"children":1388},{"style":706},[1389],{"type":30,"value":1136},{"type":20,"tag":98,"props":1391,"children":1392},{"style":105},[1393],{"type":30,"value":714},{"type":20,"tag":98,"props":1395,"children":1396},{"style":126},[1397],{"type":30,"value":1145},{"type":20,"tag":98,"props":1399,"children":1400},{"style":105},[1401],{"type":30,"value":738},{"type":20,"tag":21,"props":1403,"children":1404},{},[1405,1407,1414],{"type":30,"value":1406},"Whew! Of course, a lot of that space is taken up by the various startup images - and if we wanted to display 'landscape' startup images as well, it would be twice as long. The launch screen image size you will want can be drawn from the ",{"type":20,"tag":25,"props":1408,"children":1411},{"href":1409,"rel":1410},"https://developer.apple.com/ios/human-interface-guidelines/icons-and-images/launch-screen/",[50],[1412],{"type":30,"value":1413},"Apple Human Interface Guidelines document",{"type":30,"value":1415}," - the specific media queries to use for each is magic culled from some trial and error - you will likely have to perform some testing of your own.",{"type":20,"tag":21,"props":1417,"children":1418},{},[1419,1421,1427,1429,1435,1437,1443],{"type":30,"value":1420},"Above those, you'll see the meta tags that make our web app 'PWA-like' on iOS - particularly ",{"type":20,"tag":94,"props":1422,"children":1424},{"className":1423},[],[1425],{"type":30,"value":1426},"apple-mobile-web-app-capable",{"type":30,"value":1428},". Include these, and your users can use the Web Clip capability in iOS Safari to 'Save to Home Screen' your site. You'll want your icon specified as well - it can be any square resolution (e.g. 180x180 pixels) - and you'll want to specify the application name with the ",{"type":20,"tag":94,"props":1430,"children":1432},{"className":1431},[],[1433],{"type":30,"value":1434},"apple-mobile-web-app-title",{"type":30,"value":1436}," tag - otherwise the contents of the ",{"type":20,"tag":94,"props":1438,"children":1440},{"className":1439},[],[1441],{"type":30,"value":1442},"title",{"type":30,"value":1444}," tag are used.",{"type":20,"tag":21,"props":1446,"children":1447},{},[1448,1450,1456],{"type":30,"value":1449},"Finally, take a look at the ",{"type":20,"tag":94,"props":1451,"children":1453},{"className":1452},[],[1454],{"type":30,"value":1455},"apple-mobile-web-app-status-bar-style",{"type":30,"value":1457}," tag - we're using it to declare that we want the status bar to overlap our application, rather than push it down. The other options - which you can find in the Web Clip specification link, above - will set it's colour to white or black, and push your content down below it. Unfortunately, there's no way to gain every last pixel yet.",{"type":20,"tag":35,"props":1459,"children":1461},{"id":1460},"cache-this",[1462],{"type":30,"value":1463},"Cache This",{"type":20,"tag":21,"props":1465,"children":1466},{},[1467],{"type":30,"value":1468},"There's a substantial item missing from the iOS support set that we have on Android - caching for offline support. No Service Workers, no offline support. Well, now what?",{"type":20,"tag":21,"props":1470,"children":1471},{},[1472,1474,1481],{"type":30,"value":1473},"Luckily, if this is the make-or-break item, we can make it - using the older ",{"type":20,"tag":25,"props":1475,"children":1478},{"href":1476,"rel":1477},"https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache",[50],[1479],{"type":30,"value":1480},"Application Cache API",{"type":30,"value":1482},". Using the application cache is a topic unto itself, so the only thing I'll say here is that you'll need to be careful to only serve the application manifest to iOS devices (and you'll want to check that you're only serving it to iOS devices without Service Worker support, for future-proofing purposes).",{"type":20,"tag":21,"props":1484,"children":1485},{},[1486,1488,1495,1497,1504],{"type":30,"value":1487},"Application Cache and Service Workers don't play nice together (yet - watch ",{"type":20,"tag":25,"props":1489,"children":1492},{"href":1490,"rel":1491},"https://crbug.com/410665",[50],[1493],{"type":30,"value":1494},"this Chromium bug",{"type":30,"value":1496}," for some details on progress for that project). One day, the ",{"type":20,"tag":25,"props":1498,"children":1501},{"href":1499,"rel":1500},"https://www.w3.org/TR/service-workers/#activation-algorithm",[50],[1502],{"type":30,"value":1503},"W3C suggested behaviour",{"type":30,"value":1505}," is that browsers will ignore and/or garbage-collect the application cache when a service worker is registered and takes over caching, but that day is not yet here, and you don't want two competing technologies caching things and producing unexpected results.",{"type":20,"tag":35,"props":1507,"children":1509},{"id":1508},"are-we-there-yet",[1510],{"type":30,"value":1511},"Are we there Yet?",{"type":20,"tag":21,"props":1513,"children":1514},{},[1515,1517,1522],{"type":30,"value":1516},"This time, we really are - together with ",{"type":20,"tag":25,"props":1518,"children":1519},{"href":8},[1520],{"type":30,"value":1521},"Part 1",{"type":30,"value":1523},", you'll now have a functioning web application that can also be installed to the home screen on Android like a native application, and almost-like-native on iOS; and you're ready for the brave future where iOS becomes fully PWA-capable; AND you have an application that can still be accessed anywhere, by anyone via the Internet.",{"type":20,"tag":21,"props":1525,"children":1526},{},[1527],{"type":30,"value":1528},"Not a bad day's work. Go get yourself a drink.",{"type":20,"tag":1530,"props":1531,"children":1532},"style",{},[1533],{"type":30,"value":1534},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":137,"depth":137,"links":1536},[1537,1538,1539,1540],{"id":37,"depth":111,"text":40},{"id":751,"depth":111,"text":754},{"id":1460,"depth":111,"text":1463},{"id":1508,"depth":111,"text":1511},"markdown","content:ckeefer:2017-3:MorePWAToYa-Part2.md","content","ckeefer/2017-3/MorePWAToYa-Part2.md","ckeefer/2017-3/MorePWAToYa-Part2","md",{"user":1548,"name":1549},"ckeefer","Christopher Keefer",{"_path":1551,"_dir":1552,"_draft":7,"_partial":7,"_locale":8,"title":1553,"description":1554,"excerpt":1554,"publishDate":1555,"tags":1556,"body":1557,"_type":1541,"_id":4888,"_source":1543,"_file":4889,"_stem":4890,"_extension":1546,"author":4891},"/ckeefer/2017-2/morepwatoya-part1","2017-2","More PWA to Ya! (Progressive Web Apps, Part 1)","It's project kickoff time, and you're having a conversation with your client about what form the application will take:","2017-02-01",[13,12,14],{"type":17,"children":1558,"toc":4877},[1559,1563,1659,1673,1678,1690,1695,1700,1706,1720,1725,1733,1747,1752,1814,1819,1854,1860,1865,1894,2208,2213,2362,2383,2389,2394,2415,3299,3305,3310,4712,4717,4724,4729,4742,4748,4761,4775,4796,4802,4816,4821,4849,4854,4868,4873],{"type":20,"tag":21,"props":1560,"children":1561},{},[1562],{"type":30,"value":1554},{"type":20,"tag":1564,"props":1565,"children":1566},"blockquote",{},[1567,1577,1587,1596,1605,1614,1623,1632,1641,1650],{"type":20,"tag":21,"props":1568,"children":1569},{},[1570,1575],{"type":20,"tag":619,"props":1571,"children":1572},{},[1573],{"type":30,"value":1574},"Client",{"type":30,"value":1576},": I'm thinking mobile app. Our users will definitely be using this on the go.",{"type":20,"tag":21,"props":1578,"children":1579},{},[1580,1585],{"type":20,"tag":619,"props":1581,"children":1582},{},[1583],{"type":30,"value":1584},"Dev",{"type":30,"value":1586},": Sure, we can do a native mobile-",{"type":20,"tag":21,"props":1588,"children":1589},{},[1590,1594],{"type":20,"tag":619,"props":1591,"children":1592},{},[1593],{"type":30,"value":1574},{"type":30,"value":1595},": Mind you, we'll want a desktop version too. We'll need to use it from the office.",{"type":20,"tag":21,"props":1597,"children":1598},{},[1599,1603],{"type":20,"tag":619,"props":1600,"children":1601},{},[1602],{"type":30,"value":1584},{"type":30,"value":1604},": Okay, well, a responsive web app-",{"type":20,"tag":21,"props":1606,"children":1607},{},[1608,1612],{"type":20,"tag":619,"props":1609,"children":1610},{},[1611],{"type":30,"value":1574},{"type":30,"value":1613},": One of our priorities is definitely ease of access - we'll need the app accessible from the home screen, 'cause who has time for typing in URLs, amirite? We'll also want it to be useable offline, whenever people want to.",{"type":20,"tag":21,"props":1615,"children":1616},{},[1617,1621],{"type":20,"tag":619,"props":1618,"children":1619},{},[1620],{"type":30,"value":1584},{"type":30,"value":1622},": Ye-yeah, no problem, we can wrap your web app in a webview, bundle it up as a native app, and-",{"type":20,"tag":21,"props":1624,"children":1625},{},[1626,1630],{"type":20,"tag":619,"props":1627,"children":1628},{},[1629],{"type":30,"value":1574},{"type":30,"value":1631},": Yeah, cool. So they'll just be able to go to the site and install the app, right?",{"type":20,"tag":21,"props":1633,"children":1634},{},[1635,1639],{"type":20,"tag":619,"props":1636,"children":1637},{},[1638],{"type":30,"value":1584},{"type":30,"value":1640},": Well, no, they'll have to download it from the appropriate App Store.",{"type":20,"tag":21,"props":1642,"children":1643},{},[1644,1648],{"type":20,"tag":619,"props":1645,"children":1646},{},[1647],{"type":30,"value":1574},{"type":30,"value":1649},": Eh, that's a no-go - this is internal only, we can't have it showing up in the app stores. Didn't I make that clear from the start?",{"type":20,"tag":21,"props":1651,"children":1652},{},[1653,1657],{"type":20,"tag":619,"props":1654,"children":1655},{},[1656],{"type":30,"value":1584},{"type":30,"value":1658},": ...",{"type":20,"tag":21,"props":1660,"children":1661},{},[1662,1664,1671],{"type":30,"value":1663},"The term your client was looking for is ",{"type":20,"tag":25,"props":1665,"children":1668},{"href":1666,"rel":1667},"https://developers.google.com/web/progressive-web-apps/",[50],[1669],{"type":30,"value":1670},"Progressive Web App",{"type":30,"value":1672}," - an application that acts like a responsive web app when accessed from the browser on any device, but can be installed to mobile devices like a native application. The link above makes the case for PWAs, so we won't belabour the point - if you're still here, it's because you're convinced it's time to build a PWA.",{"type":20,"tag":21,"props":1674,"children":1675},{},[1676],{"type":30,"value":1677},"Instead, let's dig into the details. We're going to assume you have, or are building, a responsive or mobile-focused web application, and want to convert it to a PWA. Keep in mind that, like wrapping a webapp in a webview, all the heavy lifting is still done by you, the developer, in CSS, HTML and JS - there's no PWA magic to make it look 'native'.",{"type":20,"tag":21,"props":1679,"children":1680},{},[1681,1683,1688],{"type":30,"value":1682},"Well, actually, there is a ",{"type":20,"tag":619,"props":1684,"children":1685},{},[1686],{"type":30,"value":1687},"little",{"type":30,"value":1689}," magic. We'll get to that next time.",{"type":20,"tag":21,"props":1691,"children":1692},{},[1693],{"type":30,"value":1694},"Part 1 will focus on implementing a PWA the standards-compliant way. In Part 2, we'll address the 'little bit of magic' PWA's can have on Android to appear more native, and PWA's on iOS Safari, because it always has to be a special snowflake.",{"type":20,"tag":21,"props":1696,"children":1697},{},[1698],{"type":30,"value":1699},"Let's begin.",{"type":20,"tag":35,"props":1701,"children":1703},{"id":1702},"service-please",[1704],{"type":30,"value":1705},"Service, Please",{"type":20,"tag":21,"props":1707,"children":1708},{},[1709,1711,1718],{"type":30,"value":1710},"Familiar with ",{"type":20,"tag":25,"props":1712,"children":1715},{"href":1713,"rel":1714},"https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API",[50],[1716],{"type":30,"value":1717},"Service Workers",{"type":30,"value":1719},"? If not, get ready to do some reading on them - Service Workers are the clockwork that make PWAs tick.",{"type":20,"tag":21,"props":1721,"children":1722},{},[1723],{"type":30,"value":1724},"Mozilla's summary of Service Workers make it clear how this is so - and also, how complex a Service Worker can be:",{"type":20,"tag":1564,"props":1726,"children":1727},{},[1728],{"type":20,"tag":21,"props":1729,"children":1730},{},[1731],{"type":30,"value":1732},"Service workers essentially act as proxy servers that sit between web applications, and the browser and network (when available). They are intended to (amongst other things) enable the creation of effective offline experiences, intercepting network requests and taking appropriate action based on whether the network is available and updated assets reside on the server. They will also allow access to push notifications and background sync APIs.",{"type":20,"tag":21,"props":1734,"children":1735},{},[1736,1738,1745],{"type":30,"value":1737},"If you're familiar with ",{"type":20,"tag":25,"props":1739,"children":1742},{"href":1740,"rel":1741},"https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers",[50],[1743],{"type":30,"value":1744},"Web Workers",{"type":30,"value":1746},", you're about half-way there - Service Workers run on their own 'thread' (actual implementation details are up to the browser, of course), and have no access to the DOM, like a Web Worker. They have significantly more power than a web worker, however, particularly in terms of interacting with network requests; and, as a result, have more requirements to meet.",{"type":20,"tag":21,"props":1748,"children":1749},{},[1750],{"type":30,"value":1751},"Let's run down the checklist, and then we'll get into some implementation details. For a PWA's Service Worker you will need:",{"type":20,"tag":1753,"props":1754,"children":1755},"ul",{},[1756,1768,1787],{"type":20,"tag":61,"props":1757,"children":1758},{},[1759,1761,1766],{"type":30,"value":1760},"A secure context - Service Workers must be run from a TLS-secured domain (https), because they can be Men-in-the-middle on every request from the browser for a given domain. The ",{"type":20,"tag":619,"props":1762,"children":1763},{},[1764],{"type":30,"value":1765},"localhost",{"type":30,"value":1767}," special-case domain is the only exception to this rule, for the sake of development.",{"type":20,"tag":61,"props":1769,"children":1770},{},[1771,1773,1778,1780,1785],{"type":30,"value":1772},"A full list of the items you need to cache (for the caching and serving from cache functionality of a Service Worker, which is a requirement to have your application considered a PWA). Wildcards can't be used - you need to give the full (relative) path of the resource you want cached. If you're familiar with the ",{"type":20,"tag":25,"props":1774,"children":1776},{"href":1476,"rel":1775},[50],[1777],{"type":30,"value":1480},{"type":30,"value":1779}," this restriction will be familiar to you. For your application to be considered a PWA, at ",{"type":20,"tag":619,"props":1781,"children":1782},{},[1783],{"type":30,"value":1784},"least",{"type":30,"value":1786}," the start url must completely load when the user is offline.",{"type":20,"tag":61,"props":1788,"children":1789},{},[1790,1792,1798,1800,1805,1807,1812],{"type":30,"value":1791},"A means of serving the Service Worker from the root of the path that you want the Service Worker to have control over - so, if you want the Service Worker to be able to control and serve resources for your entire application, and your application is at ",{"type":20,"tag":25,"props":1793,"children":1796},{"href":1794,"rel":1795},"https://app.example.com/",[50],[1797],{"type":30,"value":1794},{"type":30,"value":1799},", you will need to be able to serve the Service Worker from ",{"type":20,"tag":619,"props":1801,"children":1802},{},[1803],{"type":30,"value":1804},"/",{"type":30,"value":1806}," (as opposed to, e.g. ",{"type":20,"tag":619,"props":1808,"children":1809},{},[1810],{"type":30,"value":1811},"/static/js/workers/",{"type":30,"value":1813}," - if you serve from there, the only resources the Service Worker will be able to control will be those under that path).",{"type":20,"tag":21,"props":1815,"children":1816},{},[1817],{"type":30,"value":1818},"Additional checklist items for a PWA include:",{"type":20,"tag":1753,"props":1820,"children":1821},{},[1822,1827,1840],{"type":20,"tag":61,"props":1823,"children":1824},{},[1825],{"type":30,"value":1826},"A responsive (or mobile-focused) design.",{"type":20,"tag":61,"props":1828,"children":1829},{},[1830,1832,1838],{"type":30,"value":1831},"Quick initial load - Google, the company behind the original PWA spec (you may have heard of them), ",{"type":20,"tag":1833,"props":1834,"children":1835},"strong",{},[1836],{"type":30,"value":1837},"strongly",{"type":30,"value":1839}," suggests that your start url load under 10 seconds on a simulated 3G network - so no loading a half-dozen affiliate advertisements.",{"type":20,"tag":61,"props":1841,"children":1842},{},[1843,1845,1852],{"type":30,"value":1844},"You will want to consider making your application a ",{"type":20,"tag":25,"props":1846,"children":1849},{"href":1847,"rel":1848},"https://en.wikipedia.org/wiki/Single-page_application",[50],[1850],{"type":30,"value":1851},"SPA",{"type":30,"value":1853}," - it's generally a good fit for this use case, especially if it allows you to cache more up-front, or trim down the number of bits your application needs to transfer over the network.",{"type":20,"tag":35,"props":1855,"children":1857},{"id":1856},"looking-for-a-hard-worker-room-and-board-provided",[1858],{"type":30,"value":1859},"Looking for a Hard Worker, Room and Board Provided",{"type":20,"tag":21,"props":1861,"children":1862},{},[1863],{"type":30,"value":1864},"First things first, let's set up our web server to serve that Service Worker (lot of variations on 'serve' in that sentence) from the root of our domain, so we can control all resources and requests therein. We're going to assume you're using Python and Django in the example below, but the principle will always be the same.",{"type":20,"tag":21,"props":1866,"children":1867},{},[1868,1870,1876,1878,1884,1886,1892],{"type":30,"value":1869},"First, we'll create a service worker named ",{"type":20,"tag":94,"props":1871,"children":1873},{"className":1872},[],[1874],{"type":30,"value":1875},"CacheWorker.js",{"type":30,"value":1877}," in our ",{"type":20,"tag":94,"props":1879,"children":1881},{"className":1880},[],[1882],{"type":30,"value":1883},"static",{"type":30,"value":1885}," directory, under a ",{"type":20,"tag":94,"props":1887,"children":1889},{"className":1888},[],[1890],{"type":30,"value":1891},"serviceWorkers",{"type":30,"value":1893}," directory. Then, we can create the view to serve this worker (and any others we might want to serve with potential control over all requests):",{"type":20,"tag":87,"props":1895,"children":1899},{"className":1896,"code":1897,"language":1898,"meta":8,"style":8},"language-python shiki shiki-themes github-light github-dark","from django.conf import settings\n\ndef serve_worker(request, worker_name):\n    \"\"\"\n    Serve the requested service worker from the appropriate location in the static files.\n    We need to serve the worker this way in order to allow it access to requests made against the\n    root - whatever /sub/dir the worker ends up getting served from is the only location it will\n    have visibility on, so serving from / is the only way to ensure the worker has visibility on all\n    requests. Only a-zA-Z-_ characters can appear in the service worker name.\n\n    :param request:\n    :param worker_name:\n    :return:\n    \"\"\"\n    worker_path = path.join(settings.STATIC_ROOT, 'serviceWorkers', \"{}.js\".format(worker_name))\n    try:\n        with open(worker_path, 'r') as worker_file:\n            return HttpResponse(worker_file, content_type='application/javascript')\n    except IOError:\n        return HttpResponseNotFound()\n","python",[1900],{"type":20,"tag":94,"props":1901,"children":1902},{"__ignoreMap":8},[1903,1927,1934,1952,1960,1968,1976,1984,1992,2000,2007,2015,2023,2031,2038,2094,2107,2145,2178,2195],{"type":20,"tag":98,"props":1904,"children":1905},{"class":100,"line":101},[1906,1912,1917,1922],{"type":20,"tag":98,"props":1907,"children":1909},{"style":1908},"--shiki-default:#D73A49;--shiki-dark:#F97583",[1910],{"type":30,"value":1911},"from",{"type":20,"tag":98,"props":1913,"children":1914},{"style":105},[1915],{"type":30,"value":1916}," django.conf ",{"type":20,"tag":98,"props":1918,"children":1919},{"style":1908},[1920],{"type":30,"value":1921},"import",{"type":20,"tag":98,"props":1923,"children":1924},{"style":105},[1925],{"type":30,"value":1926}," settings\n",{"type":20,"tag":98,"props":1928,"children":1929},{"class":100,"line":111},[1930],{"type":20,"tag":98,"props":1931,"children":1932},{"emptyLinePlaceholder":989},[1933],{"type":30,"value":992},{"type":20,"tag":98,"props":1935,"children":1936},{"class":100,"line":137},[1937,1942,1947],{"type":20,"tag":98,"props":1938,"children":1939},{"style":1908},[1940],{"type":30,"value":1941},"def",{"type":20,"tag":98,"props":1943,"children":1944},{"style":706},[1945],{"type":30,"value":1946}," serve_worker",{"type":20,"tag":98,"props":1948,"children":1949},{"style":105},[1950],{"type":30,"value":1951},"(request, worker_name):\n",{"type":20,"tag":98,"props":1953,"children":1954},{"class":100,"line":159},[1955],{"type":20,"tag":98,"props":1956,"children":1957},{"style":126},[1958],{"type":30,"value":1959},"    \"\"\"\n",{"type":20,"tag":98,"props":1961,"children":1962},{"class":100,"line":181},[1963],{"type":20,"tag":98,"props":1964,"children":1965},{"style":126},[1966],{"type":30,"value":1967},"    Serve the requested service worker from the appropriate location in the static files.\n",{"type":20,"tag":98,"props":1969,"children":1970},{"class":100,"line":202},[1971],{"type":20,"tag":98,"props":1972,"children":1973},{"style":126},[1974],{"type":30,"value":1975},"    We need to serve the worker this way in order to allow it access to requests made against the\n",{"type":20,"tag":98,"props":1977,"children":1978},{"class":100,"line":224},[1979],{"type":20,"tag":98,"props":1980,"children":1981},{"style":126},[1982],{"type":30,"value":1983},"    root - whatever /sub/dir the worker ends up getting served from is the only location it will\n",{"type":20,"tag":98,"props":1985,"children":1986},{"class":100,"line":246},[1987],{"type":20,"tag":98,"props":1988,"children":1989},{"style":126},[1990],{"type":30,"value":1991},"    have visibility on, so serving from / is the only way to ensure the worker has visibility on all\n",{"type":20,"tag":98,"props":1993,"children":1994},{"class":100,"line":260},[1995],{"type":20,"tag":98,"props":1996,"children":1997},{"style":126},[1998],{"type":30,"value":1999},"    requests. Only a-zA-Z-_ characters can appear in the service worker name.\n",{"type":20,"tag":98,"props":2001,"children":2002},{"class":100,"line":269},[2003],{"type":20,"tag":98,"props":2004,"children":2005},{"emptyLinePlaceholder":989},[2006],{"type":30,"value":992},{"type":20,"tag":98,"props":2008,"children":2009},{"class":100,"line":291},[2010],{"type":20,"tag":98,"props":2011,"children":2012},{"style":126},[2013],{"type":30,"value":2014},"    :param request:\n",{"type":20,"tag":98,"props":2016,"children":2017},{"class":100,"line":313},[2018],{"type":20,"tag":98,"props":2019,"children":2020},{"style":126},[2021],{"type":30,"value":2022},"    :param worker_name:\n",{"type":20,"tag":98,"props":2024,"children":2025},{"class":100,"line":331},[2026],{"type":20,"tag":98,"props":2027,"children":2028},{"style":126},[2029],{"type":30,"value":2030},"    :return:\n",{"type":20,"tag":98,"props":2032,"children":2033},{"class":100,"line":340},[2034],{"type":20,"tag":98,"props":2035,"children":2036},{"style":126},[2037],{"type":30,"value":1959},{"type":20,"tag":98,"props":2039,"children":2040},{"class":100,"line":348},[2041,2046,2050,2055,2060,2065,2070,2074,2079,2084,2089],{"type":20,"tag":98,"props":2042,"children":2043},{"style":105},[2044],{"type":30,"value":2045},"    worker_path ",{"type":20,"tag":98,"props":2047,"children":2048},{"style":1908},[2049],{"type":30,"value":714},{"type":20,"tag":98,"props":2051,"children":2052},{"style":105},[2053],{"type":30,"value":2054}," path.join(settings.",{"type":20,"tag":98,"props":2056,"children":2057},{"style":115},[2058],{"type":30,"value":2059},"STATIC_ROOT",{"type":20,"tag":98,"props":2061,"children":2062},{"style":105},[2063],{"type":30,"value":2064},", ",{"type":20,"tag":98,"props":2066,"children":2067},{"style":126},[2068],{"type":30,"value":2069},"'serviceWorkers'",{"type":20,"tag":98,"props":2071,"children":2072},{"style":105},[2073],{"type":30,"value":2064},{"type":20,"tag":98,"props":2075,"children":2076},{"style":126},[2077],{"type":30,"value":2078},"\"",{"type":20,"tag":98,"props":2080,"children":2081},{"style":115},[2082],{"type":30,"value":2083},"{}",{"type":20,"tag":98,"props":2085,"children":2086},{"style":126},[2087],{"type":30,"value":2088},".js\"",{"type":20,"tag":98,"props":2090,"children":2091},{"style":105},[2092],{"type":30,"value":2093},".format(worker_name))\n",{"type":20,"tag":98,"props":2095,"children":2096},{"class":100,"line":369},[2097,2102],{"type":20,"tag":98,"props":2098,"children":2099},{"style":1908},[2100],{"type":30,"value":2101},"    try",{"type":20,"tag":98,"props":2103,"children":2104},{"style":105},[2105],{"type":30,"value":2106},":\n",{"type":20,"tag":98,"props":2108,"children":2109},{"class":100,"line":389},[2110,2115,2120,2125,2130,2135,2140],{"type":20,"tag":98,"props":2111,"children":2112},{"style":1908},[2113],{"type":30,"value":2114},"        with",{"type":20,"tag":98,"props":2116,"children":2117},{"style":115},[2118],{"type":30,"value":2119}," open",{"type":20,"tag":98,"props":2121,"children":2122},{"style":105},[2123],{"type":30,"value":2124},"(worker_path, ",{"type":20,"tag":98,"props":2126,"children":2127},{"style":126},[2128],{"type":30,"value":2129},"'r'",{"type":20,"tag":98,"props":2131,"children":2132},{"style":105},[2133],{"type":30,"value":2134},") ",{"type":20,"tag":98,"props":2136,"children":2137},{"style":1908},[2138],{"type":30,"value":2139},"as",{"type":20,"tag":98,"props":2141,"children":2142},{"style":105},[2143],{"type":30,"value":2144}," worker_file:\n",{"type":20,"tag":98,"props":2146,"children":2147},{"class":100,"line":406},[2148,2153,2158,2164,2168,2173],{"type":20,"tag":98,"props":2149,"children":2150},{"style":1908},[2151],{"type":30,"value":2152},"            return",{"type":20,"tag":98,"props":2154,"children":2155},{"style":105},[2156],{"type":30,"value":2157}," HttpResponse(worker_file, ",{"type":20,"tag":98,"props":2159,"children":2161},{"style":2160},"--shiki-default:#E36209;--shiki-dark:#FFAB70",[2162],{"type":30,"value":2163},"content_type",{"type":20,"tag":98,"props":2165,"children":2166},{"style":1908},[2167],{"type":30,"value":714},{"type":20,"tag":98,"props":2169,"children":2170},{"style":126},[2171],{"type":30,"value":2172},"'application/javascript'",{"type":20,"tag":98,"props":2174,"children":2175},{"style":105},[2176],{"type":30,"value":2177},")\n",{"type":20,"tag":98,"props":2179,"children":2180},{"class":100,"line":414},[2181,2186,2191],{"type":20,"tag":98,"props":2182,"children":2183},{"style":1908},[2184],{"type":30,"value":2185},"    except",{"type":20,"tag":98,"props":2187,"children":2188},{"style":115},[2189],{"type":30,"value":2190}," IOError",{"type":20,"tag":98,"props":2192,"children":2193},{"style":105},[2194],{"type":30,"value":2106},{"type":20,"tag":98,"props":2196,"children":2197},{"class":100,"line":422},[2198,2203],{"type":20,"tag":98,"props":2199,"children":2200},{"style":1908},[2201],{"type":30,"value":2202},"        return",{"type":20,"tag":98,"props":2204,"children":2205},{"style":105},[2206],{"type":30,"value":2207}," HttpResponseNotFound()\n",{"type":20,"tag":21,"props":2209,"children":2210},{},[2211],{"type":30,"value":2212},"Next, in urls.py, we'll add the route to this view:",{"type":20,"tag":87,"props":2214,"children":2216},{"className":1896,"code":2215,"language":1898,"meta":8,"style":8},"urlpatterns = [\n    # ...other patterns...\n    url(r'^worker-(?P\u003Cworker_name>[a-zA-Z\\-_]+).js$', views.serve_worker, name='serve_worker'),\n    # ...other patterns...\n]\n",[2217],{"type":20,"tag":94,"props":2218,"children":2219},{"__ignoreMap":8},[2220,2237,2245,2347,2354],{"type":20,"tag":98,"props":2221,"children":2222},{"class":100,"line":101},[2223,2228,2232],{"type":20,"tag":98,"props":2224,"children":2225},{"style":105},[2226],{"type":30,"value":2227},"urlpatterns ",{"type":20,"tag":98,"props":2229,"children":2230},{"style":1908},[2231],{"type":30,"value":714},{"type":20,"tag":98,"props":2233,"children":2234},{"style":105},[2235],{"type":30,"value":2236}," [\n",{"type":20,"tag":98,"props":2238,"children":2239},{"class":100,"line":111},[2240],{"type":20,"tag":98,"props":2241,"children":2242},{"style":686},[2243],{"type":30,"value":2244},"    # ...other patterns...\n",{"type":20,"tag":98,"props":2246,"children":2247},{"class":100,"line":137},[2248,2253,2258,2263,2268,2274,2279,2284,2289,2295,2300,2305,2310,2315,2320,2324,2329,2333,2337,2342],{"type":20,"tag":98,"props":2249,"children":2250},{"style":105},[2251],{"type":30,"value":2252},"    url(",{"type":20,"tag":98,"props":2254,"children":2255},{"style":1908},[2256],{"type":30,"value":2257},"r",{"type":20,"tag":98,"props":2259,"children":2260},{"style":126},[2261],{"type":30,"value":2262},"'",{"type":20,"tag":98,"props":2264,"children":2265},{"style":115},[2266],{"type":30,"value":2267},"^",{"type":20,"tag":98,"props":2269,"children":2271},{"style":2270},"--shiki-default:#032F62;--shiki-dark:#DBEDFF",[2272],{"type":30,"value":2273},"worker-",{"type":20,"tag":98,"props":2275,"children":2276},{"style":115},[2277],{"type":30,"value":2278},"(",{"type":20,"tag":98,"props":2280,"children":2281},{"style":700},[2282],{"type":30,"value":2283},"?P\u003Cworker_name>",{"type":20,"tag":98,"props":2285,"children":2286},{"style":115},[2287],{"type":30,"value":2288},"[a-zA-Z",{"type":20,"tag":98,"props":2290,"children":2292},{"style":2291},"--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold",[2293],{"type":30,"value":2294},"\\-",{"type":20,"tag":98,"props":2296,"children":2297},{"style":115},[2298],{"type":30,"value":2299},"_]",{"type":20,"tag":98,"props":2301,"children":2302},{"style":1908},[2303],{"type":30,"value":2304},"+",{"type":20,"tag":98,"props":2306,"children":2307},{"style":115},[2308],{"type":30,"value":2309},").",{"type":20,"tag":98,"props":2311,"children":2312},{"style":2270},[2313],{"type":30,"value":2314},"js",{"type":20,"tag":98,"props":2316,"children":2317},{"style":115},[2318],{"type":30,"value":2319},"$",{"type":20,"tag":98,"props":2321,"children":2322},{"style":126},[2323],{"type":30,"value":2262},{"type":20,"tag":98,"props":2325,"children":2326},{"style":105},[2327],{"type":30,"value":2328},", views.serve_worker, ",{"type":20,"tag":98,"props":2330,"children":2331},{"style":2160},[2332],{"type":30,"value":546},{"type":20,"tag":98,"props":2334,"children":2335},{"style":1908},[2336],{"type":30,"value":714},{"type":20,"tag":98,"props":2338,"children":2339},{"style":126},[2340],{"type":30,"value":2341},"'serve_worker'",{"type":20,"tag":98,"props":2343,"children":2344},{"style":105},[2345],{"type":30,"value":2346},"),\n",{"type":20,"tag":98,"props":2348,"children":2349},{"class":100,"line":159},[2350],{"type":20,"tag":98,"props":2351,"children":2352},{"style":686},[2353],{"type":30,"value":2244},{"type":20,"tag":98,"props":2355,"children":2356},{"class":100,"line":181},[2357],{"type":20,"tag":98,"props":2358,"children":2359},{"style":105},[2360],{"type":30,"value":2361},"]\n",{"type":20,"tag":21,"props":2363,"children":2364},{},[2365,2367,2373,2375,2381],{"type":30,"value":2366},"Now, assuming our domain was ",{"type":20,"tag":94,"props":2368,"children":2370},{"className":2369},[],[2371],{"type":30,"value":2372},"https://app.example.com",{"type":30,"value":2374},", a request to ",{"type":20,"tag":94,"props":2376,"children":2378},{"className":2377},[],[2379],{"type":30,"value":2380},"https://app.example.com/worker-cacheWorker.js",{"type":30,"value":2382}," will return our worker script.",{"type":20,"tag":35,"props":2384,"children":2386},{"id":2385},"putting-your-workers-in-their-place",[2387],{"type":30,"value":2388},"Putting Your Workers in their Place",{"type":20,"tag":21,"props":2390,"children":2391},{},[2392],{"type":30,"value":2393},"Now that we're serving our worker script from the desired location, we need to tell the browser that it should be requesting said worker script, and installing it as a Service Worker.",{"type":20,"tag":21,"props":2395,"children":2396},{},[2397,2399,2406,2408,2413],{"type":30,"value":2398},"To this effect, we will want to use the ",{"type":20,"tag":25,"props":2400,"children":2403},{"href":2401,"rel":2402},"https://developer.mozilla.org/en-US/docs/Web/API/Navigator/serviceWorker",[50],[2404],{"type":30,"value":2405},"ServiceWorkerContainer",{"type":30,"value":2407}," API to register our service worker. Of course, since our PWA is a ",{"type":20,"tag":619,"props":2409,"children":2410},{},[2411],{"type":30,"value":2412},"progressive",{"type":30,"value":2414}," enhancement, we will check to ensure that the browser actually supports Service Workers before we try and install it - your application should have some fallback behaviour when it encounters a browser that doesn't.",{"type":20,"tag":87,"props":2416,"children":2419},{"className":2417,"code":2418,"language":2314,"meta":8,"style":8},"language-js shiki shiki-themes github-light github-dark","/**\n * Install service workers in those browsers which support them.\n */\n(function(window){\n    var serviceWorkers = {\n        \"IMMEDIATE\": [],\n        \"LOAD\": ['cacheWorker'],\n        \"DELAY\": []\n    };\n\n    /**\n     * Attempt to register the worker, and log either the success or failure to the console.\n     * @param {String} worker\n     */\n    function registerWorker(worker){\n        window.navigator.serviceWorker.register('/worker-'+worker+'.js').then(function(reg){\n            console.log('Registration successful for worker '+worker+', with scope: ' + reg.scope);\n        }, function(error){\n            console.log('Service Worker registration failed for worker: ', worker, error);\n        });\n    }\n\n    /**\n     * Handle messages sent to the main thread by Service Workers.\n     * @param event\n     */\n    function handleMessage(event){\n        console.log(\"TODO: Your app should do something with the event data sent by the worker.\", event.data.message, event.data.data);\n    }\n\n    // Check for ServiceWorker support.\n    if ('serviceWorker' in window.navigator){\n        // Listen for messages broadcasted by any service worker\n        window.navigator.serviceWorker.addEventListener('message', handleMessage);\n\n        /*\n        * For each service worker, consider their priority queue.\n        * Workers in the 'IMMEDIATE' queue are registered as soon as we can - this is useful if,\n        * for example, we need to immediately be able to intercept requests.\n        * Workers within queue 'LOAD' are registered after document load - this is the time to start caching\n        * resources, for example, without contending with the browser for bandwidth.\n        * Workers in queue 'DELAY' are registered after the application lets us know explicitly that now is a\n        * good time. How your application goes about doing this is up to you. This last category\n        * is good for workers that are going to be carrying out long-term activities, like\n        * long-polling a server.\n        */\n        serviceWorkers.IMMEDIATE.forEach(registerWorker);\n\n        window.addEventListener('load', function(){\n            serviceWorkers.LOAD.forEach(registerWorker);\n        });\n\n        window.addEventListener('yourCustomDelayEvent', function(){\n            serviceWorkers.DELAY.forEach(registerWorker);\n        });\n    }\n})(window);\n",[2420],{"type":20,"tag":94,"props":2421,"children":2422},{"__ignoreMap":8},[2423,2431,2439,2447,2473,2495,2508,2531,2544,2552,2559,2567,2575,2598,2606,2632,2701,2750,2775,2800,2808,2816,2823,2830,2838,2854,2861,2886,2912,2919,2926,2934,2962,2970,2996,3003,3012,3021,3030,3039,3048,3057,3066,3075,3084,3093,3102,3131,3139,3174,3200,3208,3216,3249,3274,3282,3290],{"type":20,"tag":98,"props":2424,"children":2425},{"class":100,"line":101},[2426],{"type":20,"tag":98,"props":2427,"children":2428},{"style":686},[2429],{"type":30,"value":2430},"/**\n",{"type":20,"tag":98,"props":2432,"children":2433},{"class":100,"line":111},[2434],{"type":20,"tag":98,"props":2435,"children":2436},{"style":686},[2437],{"type":30,"value":2438}," * Install service workers in those browsers which support them.\n",{"type":20,"tag":98,"props":2440,"children":2441},{"class":100,"line":137},[2442],{"type":20,"tag":98,"props":2443,"children":2444},{"style":686},[2445],{"type":30,"value":2446}," */\n",{"type":20,"tag":98,"props":2448,"children":2449},{"class":100,"line":159},[2450,2454,2459,2463,2468],{"type":20,"tag":98,"props":2451,"children":2452},{"style":105},[2453],{"type":30,"value":2278},{"type":20,"tag":98,"props":2455,"children":2456},{"style":1908},[2457],{"type":30,"value":2458},"function",{"type":20,"tag":98,"props":2460,"children":2461},{"style":105},[2462],{"type":30,"value":2278},{"type":20,"tag":98,"props":2464,"children":2465},{"style":2160},[2466],{"type":30,"value":2467},"window",{"type":20,"tag":98,"props":2469,"children":2470},{"style":105},[2471],{"type":30,"value":2472},"){\n",{"type":20,"tag":98,"props":2474,"children":2475},{"class":100,"line":181},[2476,2481,2486,2490],{"type":20,"tag":98,"props":2477,"children":2478},{"style":1908},[2479],{"type":30,"value":2480},"    var",{"type":20,"tag":98,"props":2482,"children":2483},{"style":105},[2484],{"type":30,"value":2485}," serviceWorkers ",{"type":20,"tag":98,"props":2487,"children":2488},{"style":1908},[2489],{"type":30,"value":714},{"type":20,"tag":98,"props":2491,"children":2492},{"style":105},[2493],{"type":30,"value":2494}," {\n",{"type":20,"tag":98,"props":2496,"children":2497},{"class":100,"line":202},[2498,2503],{"type":20,"tag":98,"props":2499,"children":2500},{"style":126},[2501],{"type":30,"value":2502},"        \"IMMEDIATE\"",{"type":20,"tag":98,"props":2504,"children":2505},{"style":105},[2506],{"type":30,"value":2507},": [],\n",{"type":20,"tag":98,"props":2509,"children":2510},{"class":100,"line":224},[2511,2516,2521,2526],{"type":20,"tag":98,"props":2512,"children":2513},{"style":126},[2514],{"type":30,"value":2515},"        \"LOAD\"",{"type":20,"tag":98,"props":2517,"children":2518},{"style":105},[2519],{"type":30,"value":2520},": [",{"type":20,"tag":98,"props":2522,"children":2523},{"style":126},[2524],{"type":30,"value":2525},"'cacheWorker'",{"type":20,"tag":98,"props":2527,"children":2528},{"style":105},[2529],{"type":30,"value":2530},"],\n",{"type":20,"tag":98,"props":2532,"children":2533},{"class":100,"line":246},[2534,2539],{"type":20,"tag":98,"props":2535,"children":2536},{"style":126},[2537],{"type":30,"value":2538},"        \"DELAY\"",{"type":20,"tag":98,"props":2540,"children":2541},{"style":105},[2542],{"type":30,"value":2543},": []\n",{"type":20,"tag":98,"props":2545,"children":2546},{"class":100,"line":260},[2547],{"type":20,"tag":98,"props":2548,"children":2549},{"style":105},[2550],{"type":30,"value":2551},"    };\n",{"type":20,"tag":98,"props":2553,"children":2554},{"class":100,"line":269},[2555],{"type":20,"tag":98,"props":2556,"children":2557},{"emptyLinePlaceholder":989},[2558],{"type":30,"value":992},{"type":20,"tag":98,"props":2560,"children":2561},{"class":100,"line":291},[2562],{"type":20,"tag":98,"props":2563,"children":2564},{"style":686},[2565],{"type":30,"value":2566},"    /**\n",{"type":20,"tag":98,"props":2568,"children":2569},{"class":100,"line":313},[2570],{"type":20,"tag":98,"props":2571,"children":2572},{"style":686},[2573],{"type":30,"value":2574},"     * Attempt to register the worker, and log either the success or failure to the console.\n",{"type":20,"tag":98,"props":2576,"children":2577},{"class":100,"line":331},[2578,2583,2588,2593],{"type":20,"tag":98,"props":2579,"children":2580},{"style":686},[2581],{"type":30,"value":2582},"     * ",{"type":20,"tag":98,"props":2584,"children":2585},{"style":1908},[2586],{"type":30,"value":2587},"@param",{"type":20,"tag":98,"props":2589,"children":2590},{"style":706},[2591],{"type":30,"value":2592}," {String}",{"type":20,"tag":98,"props":2594,"children":2595},{"style":105},[2596],{"type":30,"value":2597}," worker\n",{"type":20,"tag":98,"props":2599,"children":2600},{"class":100,"line":340},[2601],{"type":20,"tag":98,"props":2602,"children":2603},{"style":686},[2604],{"type":30,"value":2605},"     */\n",{"type":20,"tag":98,"props":2607,"children":2608},{"class":100,"line":348},[2609,2614,2619,2623,2628],{"type":20,"tag":98,"props":2610,"children":2611},{"style":1908},[2612],{"type":30,"value":2613},"    function",{"type":20,"tag":98,"props":2615,"children":2616},{"style":706},[2617],{"type":30,"value":2618}," registerWorker",{"type":20,"tag":98,"props":2620,"children":2621},{"style":105},[2622],{"type":30,"value":2278},{"type":20,"tag":98,"props":2624,"children":2625},{"style":2160},[2626],{"type":30,"value":2627},"worker",{"type":20,"tag":98,"props":2629,"children":2630},{"style":105},[2631],{"type":30,"value":2472},{"type":20,"tag":98,"props":2633,"children":2634},{"class":100,"line":369},[2635,2640,2645,2649,2654,2658,2662,2666,2671,2675,2680,2684,2688,2692,2697],{"type":20,"tag":98,"props":2636,"children":2637},{"style":105},[2638],{"type":30,"value":2639},"        window.navigator.serviceWorker.",{"type":20,"tag":98,"props":2641,"children":2642},{"style":706},[2643],{"type":30,"value":2644},"register",{"type":20,"tag":98,"props":2646,"children":2647},{"style":105},[2648],{"type":30,"value":2278},{"type":20,"tag":98,"props":2650,"children":2651},{"style":126},[2652],{"type":30,"value":2653},"'/worker-'",{"type":20,"tag":98,"props":2655,"children":2656},{"style":1908},[2657],{"type":30,"value":2304},{"type":20,"tag":98,"props":2659,"children":2660},{"style":105},[2661],{"type":30,"value":2627},{"type":20,"tag":98,"props":2663,"children":2664},{"style":1908},[2665],{"type":30,"value":2304},{"type":20,"tag":98,"props":2667,"children":2668},{"style":126},[2669],{"type":30,"value":2670},"'.js'",{"type":20,"tag":98,"props":2672,"children":2673},{"style":105},[2674],{"type":30,"value":2309},{"type":20,"tag":98,"props":2676,"children":2677},{"style":706},[2678],{"type":30,"value":2679},"then",{"type":20,"tag":98,"props":2681,"children":2682},{"style":105},[2683],{"type":30,"value":2278},{"type":20,"tag":98,"props":2685,"children":2686},{"style":1908},[2687],{"type":30,"value":2458},{"type":20,"tag":98,"props":2689,"children":2690},{"style":105},[2691],{"type":30,"value":2278},{"type":20,"tag":98,"props":2693,"children":2694},{"style":2160},[2695],{"type":30,"value":2696},"reg",{"type":20,"tag":98,"props":2698,"children":2699},{"style":105},[2700],{"type":30,"value":2472},{"type":20,"tag":98,"props":2702,"children":2703},{"class":100,"line":389},[2704,2709,2714,2718,2723,2727,2731,2735,2740,2745],{"type":20,"tag":98,"props":2705,"children":2706},{"style":105},[2707],{"type":30,"value":2708},"            console.",{"type":20,"tag":98,"props":2710,"children":2711},{"style":706},[2712],{"type":30,"value":2713},"log",{"type":20,"tag":98,"props":2715,"children":2716},{"style":105},[2717],{"type":30,"value":2278},{"type":20,"tag":98,"props":2719,"children":2720},{"style":126},[2721],{"type":30,"value":2722},"'Registration successful for worker '",{"type":20,"tag":98,"props":2724,"children":2725},{"style":1908},[2726],{"type":30,"value":2304},{"type":20,"tag":98,"props":2728,"children":2729},{"style":105},[2730],{"type":30,"value":2627},{"type":20,"tag":98,"props":2732,"children":2733},{"style":1908},[2734],{"type":30,"value":2304},{"type":20,"tag":98,"props":2736,"children":2737},{"style":126},[2738],{"type":30,"value":2739},"', with scope: '",{"type":20,"tag":98,"props":2741,"children":2742},{"style":1908},[2743],{"type":30,"value":2744}," +",{"type":20,"tag":98,"props":2746,"children":2747},{"style":105},[2748],{"type":30,"value":2749}," reg.scope);\n",{"type":20,"tag":98,"props":2751,"children":2752},{"class":100,"line":406},[2753,2758,2762,2766,2771],{"type":20,"tag":98,"props":2754,"children":2755},{"style":105},[2756],{"type":30,"value":2757},"        }, ",{"type":20,"tag":98,"props":2759,"children":2760},{"style":1908},[2761],{"type":30,"value":2458},{"type":20,"tag":98,"props":2763,"children":2764},{"style":105},[2765],{"type":30,"value":2278},{"type":20,"tag":98,"props":2767,"children":2768},{"style":2160},[2769],{"type":30,"value":2770},"error",{"type":20,"tag":98,"props":2772,"children":2773},{"style":105},[2774],{"type":30,"value":2472},{"type":20,"tag":98,"props":2776,"children":2777},{"class":100,"line":414},[2778,2782,2786,2790,2795],{"type":20,"tag":98,"props":2779,"children":2780},{"style":105},[2781],{"type":30,"value":2708},{"type":20,"tag":98,"props":2783,"children":2784},{"style":706},[2785],{"type":30,"value":2713},{"type":20,"tag":98,"props":2787,"children":2788},{"style":105},[2789],{"type":30,"value":2278},{"type":20,"tag":98,"props":2791,"children":2792},{"style":126},[2793],{"type":30,"value":2794},"'Service Worker registration failed for worker: '",{"type":20,"tag":98,"props":2796,"children":2797},{"style":105},[2798],{"type":30,"value":2799},", worker, error);\n",{"type":20,"tag":98,"props":2801,"children":2802},{"class":100,"line":422},[2803],{"type":20,"tag":98,"props":2804,"children":2805},{"style":105},[2806],{"type":30,"value":2807},"        });\n",{"type":20,"tag":98,"props":2809,"children":2810},{"class":100,"line":443},[2811],{"type":20,"tag":98,"props":2812,"children":2813},{"style":105},[2814],{"type":30,"value":2815},"    }\n",{"type":20,"tag":98,"props":2817,"children":2818},{"class":100,"line":463},[2819],{"type":20,"tag":98,"props":2820,"children":2821},{"emptyLinePlaceholder":989},[2822],{"type":30,"value":992},{"type":20,"tag":98,"props":2824,"children":2825},{"class":100,"line":480},[2826],{"type":20,"tag":98,"props":2827,"children":2828},{"style":686},[2829],{"type":30,"value":2566},{"type":20,"tag":98,"props":2831,"children":2832},{"class":100,"line":489},[2833],{"type":20,"tag":98,"props":2834,"children":2835},{"style":686},[2836],{"type":30,"value":2837},"     * Handle messages sent to the main thread by Service Workers.\n",{"type":20,"tag":98,"props":2839,"children":2840},{"class":100,"line":498},[2841,2845,2849],{"type":20,"tag":98,"props":2842,"children":2843},{"style":686},[2844],{"type":30,"value":2582},{"type":20,"tag":98,"props":2846,"children":2847},{"style":1908},[2848],{"type":30,"value":2587},{"type":20,"tag":98,"props":2850,"children":2851},{"style":105},[2852],{"type":30,"value":2853}," event\n",{"type":20,"tag":98,"props":2855,"children":2856},{"class":100,"line":516},[2857],{"type":20,"tag":98,"props":2858,"children":2859},{"style":686},[2860],{"type":30,"value":2605},{"type":20,"tag":98,"props":2862,"children":2863},{"class":100,"line":1273},[2864,2868,2873,2877,2882],{"type":20,"tag":98,"props":2865,"children":2866},{"style":1908},[2867],{"type":30,"value":2613},{"type":20,"tag":98,"props":2869,"children":2870},{"style":706},[2871],{"type":30,"value":2872}," handleMessage",{"type":20,"tag":98,"props":2874,"children":2875},{"style":105},[2876],{"type":30,"value":2278},{"type":20,"tag":98,"props":2878,"children":2879},{"style":2160},[2880],{"type":30,"value":2881},"event",{"type":20,"tag":98,"props":2883,"children":2884},{"style":105},[2885],{"type":30,"value":2472},{"type":20,"tag":98,"props":2887,"children":2888},{"class":100,"line":1289},[2889,2894,2898,2902,2907],{"type":20,"tag":98,"props":2890,"children":2891},{"style":105},[2892],{"type":30,"value":2893},"        console.",{"type":20,"tag":98,"props":2895,"children":2896},{"style":706},[2897],{"type":30,"value":2713},{"type":20,"tag":98,"props":2899,"children":2900},{"style":105},[2901],{"type":30,"value":2278},{"type":20,"tag":98,"props":2903,"children":2904},{"style":126},[2905],{"type":30,"value":2906},"\"TODO: Your app should do something with the event data sent by the worker.\"",{"type":20,"tag":98,"props":2908,"children":2909},{"style":105},[2910],{"type":30,"value":2911},", event.data.message, event.data.data);\n",{"type":20,"tag":98,"props":2913,"children":2914},{"class":100,"line":1297},[2915],{"type":20,"tag":98,"props":2916,"children":2917},{"style":105},[2918],{"type":30,"value":2815},{"type":20,"tag":98,"props":2920,"children":2921},{"class":100,"line":1317},[2922],{"type":20,"tag":98,"props":2923,"children":2924},{"emptyLinePlaceholder":989},[2925],{"type":30,"value":992},{"type":20,"tag":98,"props":2927,"children":2928},{"class":100,"line":1325},[2929],{"type":20,"tag":98,"props":2930,"children":2931},{"style":686},[2932],{"type":30,"value":2933},"    // Check for ServiceWorker support.\n",{"type":20,"tag":98,"props":2935,"children":2936},{"class":100,"line":1334},[2937,2942,2947,2952,2957],{"type":20,"tag":98,"props":2938,"children":2939},{"style":1908},[2940],{"type":30,"value":2941},"    if",{"type":20,"tag":98,"props":2943,"children":2944},{"style":105},[2945],{"type":30,"value":2946}," (",{"type":20,"tag":98,"props":2948,"children":2949},{"style":126},[2950],{"type":30,"value":2951},"'serviceWorker'",{"type":20,"tag":98,"props":2953,"children":2954},{"style":1908},[2955],{"type":30,"value":2956}," in",{"type":20,"tag":98,"props":2958,"children":2959},{"style":105},[2960],{"type":30,"value":2961}," window.navigator){\n",{"type":20,"tag":98,"props":2963,"children":2964},{"class":100,"line":1359},[2965],{"type":20,"tag":98,"props":2966,"children":2967},{"style":686},[2968],{"type":30,"value":2969},"        // Listen for messages broadcasted by any service worker\n",{"type":20,"tag":98,"props":2971,"children":2972},{"class":100,"line":1376},[2973,2977,2982,2986,2991],{"type":20,"tag":98,"props":2974,"children":2975},{"style":105},[2976],{"type":30,"value":2639},{"type":20,"tag":98,"props":2978,"children":2979},{"style":706},[2980],{"type":30,"value":2981},"addEventListener",{"type":20,"tag":98,"props":2983,"children":2984},{"style":105},[2985],{"type":30,"value":2278},{"type":20,"tag":98,"props":2987,"children":2988},{"style":126},[2989],{"type":30,"value":2990},"'message'",{"type":20,"tag":98,"props":2992,"children":2993},{"style":105},[2994],{"type":30,"value":2995},", handleMessage);\n",{"type":20,"tag":98,"props":2997,"children":2998},{"class":100,"line":1384},[2999],{"type":20,"tag":98,"props":3000,"children":3001},{"emptyLinePlaceholder":989},[3002],{"type":30,"value":992},{"type":20,"tag":98,"props":3004,"children":3006},{"class":100,"line":3005},36,[3007],{"type":20,"tag":98,"props":3008,"children":3009},{"style":686},[3010],{"type":30,"value":3011},"        /*\n",{"type":20,"tag":98,"props":3013,"children":3015},{"class":100,"line":3014},37,[3016],{"type":20,"tag":98,"props":3017,"children":3018},{"style":686},[3019],{"type":30,"value":3020},"        * For each service worker, consider their priority queue.\n",{"type":20,"tag":98,"props":3022,"children":3024},{"class":100,"line":3023},38,[3025],{"type":20,"tag":98,"props":3026,"children":3027},{"style":686},[3028],{"type":30,"value":3029},"        * Workers in the 'IMMEDIATE' queue are registered as soon as we can - this is useful if,\n",{"type":20,"tag":98,"props":3031,"children":3033},{"class":100,"line":3032},39,[3034],{"type":20,"tag":98,"props":3035,"children":3036},{"style":686},[3037],{"type":30,"value":3038},"        * for example, we need to immediately be able to intercept requests.\n",{"type":20,"tag":98,"props":3040,"children":3042},{"class":100,"line":3041},40,[3043],{"type":20,"tag":98,"props":3044,"children":3045},{"style":686},[3046],{"type":30,"value":3047},"        * Workers within queue 'LOAD' are registered after document load - this is the time to start caching\n",{"type":20,"tag":98,"props":3049,"children":3051},{"class":100,"line":3050},41,[3052],{"type":20,"tag":98,"props":3053,"children":3054},{"style":686},[3055],{"type":30,"value":3056},"        * resources, for example, without contending with the browser for bandwidth.\n",{"type":20,"tag":98,"props":3058,"children":3060},{"class":100,"line":3059},42,[3061],{"type":20,"tag":98,"props":3062,"children":3063},{"style":686},[3064],{"type":30,"value":3065},"        * Workers in queue 'DELAY' are registered after the application lets us know explicitly that now is a\n",{"type":20,"tag":98,"props":3067,"children":3069},{"class":100,"line":3068},43,[3070],{"type":20,"tag":98,"props":3071,"children":3072},{"style":686},[3073],{"type":30,"value":3074},"        * good time. How your application goes about doing this is up to you. This last category\n",{"type":20,"tag":98,"props":3076,"children":3078},{"class":100,"line":3077},44,[3079],{"type":20,"tag":98,"props":3080,"children":3081},{"style":686},[3082],{"type":30,"value":3083},"        * is good for workers that are going to be carrying out long-term activities, like\n",{"type":20,"tag":98,"props":3085,"children":3087},{"class":100,"line":3086},45,[3088],{"type":20,"tag":98,"props":3089,"children":3090},{"style":686},[3091],{"type":30,"value":3092},"        * long-polling a server.\n",{"type":20,"tag":98,"props":3094,"children":3096},{"class":100,"line":3095},46,[3097],{"type":20,"tag":98,"props":3098,"children":3099},{"style":686},[3100],{"type":30,"value":3101},"        */\n",{"type":20,"tag":98,"props":3103,"children":3105},{"class":100,"line":3104},47,[3106,3111,3116,3121,3126],{"type":20,"tag":98,"props":3107,"children":3108},{"style":105},[3109],{"type":30,"value":3110},"        serviceWorkers.",{"type":20,"tag":98,"props":3112,"children":3113},{"style":115},[3114],{"type":30,"value":3115},"IMMEDIATE",{"type":20,"tag":98,"props":3117,"children":3118},{"style":105},[3119],{"type":30,"value":3120},".",{"type":20,"tag":98,"props":3122,"children":3123},{"style":706},[3124],{"type":30,"value":3125},"forEach",{"type":20,"tag":98,"props":3127,"children":3128},{"style":105},[3129],{"type":30,"value":3130},"(registerWorker);\n",{"type":20,"tag":98,"props":3132,"children":3134},{"class":100,"line":3133},48,[3135],{"type":20,"tag":98,"props":3136,"children":3137},{"emptyLinePlaceholder":989},[3138],{"type":30,"value":992},{"type":20,"tag":98,"props":3140,"children":3142},{"class":100,"line":3141},49,[3143,3148,3152,3156,3161,3165,3169],{"type":20,"tag":98,"props":3144,"children":3145},{"style":105},[3146],{"type":30,"value":3147},"        window.",{"type":20,"tag":98,"props":3149,"children":3150},{"style":706},[3151],{"type":30,"value":2981},{"type":20,"tag":98,"props":3153,"children":3154},{"style":105},[3155],{"type":30,"value":2278},{"type":20,"tag":98,"props":3157,"children":3158},{"style":126},[3159],{"type":30,"value":3160},"'load'",{"type":20,"tag":98,"props":3162,"children":3163},{"style":105},[3164],{"type":30,"value":2064},{"type":20,"tag":98,"props":3166,"children":3167},{"style":1908},[3168],{"type":30,"value":2458},{"type":20,"tag":98,"props":3170,"children":3171},{"style":105},[3172],{"type":30,"value":3173},"(){\n",{"type":20,"tag":98,"props":3175,"children":3177},{"class":100,"line":3176},50,[3178,3183,3188,3192,3196],{"type":20,"tag":98,"props":3179,"children":3180},{"style":105},[3181],{"type":30,"value":3182},"            serviceWorkers.",{"type":20,"tag":98,"props":3184,"children":3185},{"style":115},[3186],{"type":30,"value":3187},"LOAD",{"type":20,"tag":98,"props":3189,"children":3190},{"style":105},[3191],{"type":30,"value":3120},{"type":20,"tag":98,"props":3193,"children":3194},{"style":706},[3195],{"type":30,"value":3125},{"type":20,"tag":98,"props":3197,"children":3198},{"style":105},[3199],{"type":30,"value":3130},{"type":20,"tag":98,"props":3201,"children":3203},{"class":100,"line":3202},51,[3204],{"type":20,"tag":98,"props":3205,"children":3206},{"style":105},[3207],{"type":30,"value":2807},{"type":20,"tag":98,"props":3209,"children":3211},{"class":100,"line":3210},52,[3212],{"type":20,"tag":98,"props":3213,"children":3214},{"emptyLinePlaceholder":989},[3215],{"type":30,"value":992},{"type":20,"tag":98,"props":3217,"children":3219},{"class":100,"line":3218},53,[3220,3224,3228,3232,3237,3241,3245],{"type":20,"tag":98,"props":3221,"children":3222},{"style":105},[3223],{"type":30,"value":3147},{"type":20,"tag":98,"props":3225,"children":3226},{"style":706},[3227],{"type":30,"value":2981},{"type":20,"tag":98,"props":3229,"children":3230},{"style":105},[3231],{"type":30,"value":2278},{"type":20,"tag":98,"props":3233,"children":3234},{"style":126},[3235],{"type":30,"value":3236},"'yourCustomDelayEvent'",{"type":20,"tag":98,"props":3238,"children":3239},{"style":105},[3240],{"type":30,"value":2064},{"type":20,"tag":98,"props":3242,"children":3243},{"style":1908},[3244],{"type":30,"value":2458},{"type":20,"tag":98,"props":3246,"children":3247},{"style":105},[3248],{"type":30,"value":3173},{"type":20,"tag":98,"props":3250,"children":3252},{"class":100,"line":3251},54,[3253,3257,3262,3266,3270],{"type":20,"tag":98,"props":3254,"children":3255},{"style":105},[3256],{"type":30,"value":3182},{"type":20,"tag":98,"props":3258,"children":3259},{"style":115},[3260],{"type":30,"value":3261},"DELAY",{"type":20,"tag":98,"props":3263,"children":3264},{"style":105},[3265],{"type":30,"value":3120},{"type":20,"tag":98,"props":3267,"children":3268},{"style":706},[3269],{"type":30,"value":3125},{"type":20,"tag":98,"props":3271,"children":3272},{"style":105},[3273],{"type":30,"value":3130},{"type":20,"tag":98,"props":3275,"children":3277},{"class":100,"line":3276},55,[3278],{"type":20,"tag":98,"props":3279,"children":3280},{"style":105},[3281],{"type":30,"value":2807},{"type":20,"tag":98,"props":3283,"children":3285},{"class":100,"line":3284},56,[3286],{"type":20,"tag":98,"props":3287,"children":3288},{"style":105},[3289],{"type":30,"value":2815},{"type":20,"tag":98,"props":3291,"children":3293},{"class":100,"line":3292},57,[3294],{"type":20,"tag":98,"props":3295,"children":3296},{"style":105},[3297],{"type":30,"value":3298},"})(window);\n",{"type":20,"tag":35,"props":3300,"children":3302},{"id":3301},"cache-me-im-falling",[3303],{"type":30,"value":3304},"Cache Me, I'm Falling",{"type":20,"tag":21,"props":3306,"children":3307},{},[3308],{"type":30,"value":3309},"So, we have our server sending our cacheWorker file along properly, and we have the browser registering the service worker, and downloading and installing our script. That's great - except, our cacheWorker script is empty, so it doesn't do anything. Let's fix that.",{"type":20,"tag":87,"props":3311,"children":3313},{"className":2417,"code":3312,"language":2314,"meta":8,"style":8},"/**\n * Service worker intended for caching and serving files when the application is offline,\n * to meet the requirements for a PWA.\n * @author Christopher Keefer\n */\nvar cacheVersion = 1,\n    staticCache = 'static-cache-v'+cacheVersion,\n    cacheableResources = [\n        // Root - This MUST be in the cacheable resources for a PWA!\n        '/',\n        // Images\n        '/static/img/yourLogo.png',\n        //... any other static image resources your application will need ...\n        // CSS\n        '/static/css/yourapp.min.css',\n        // ... any other styling your app will need, order doesn't matter ....\n        // Fonts\n        '/static/css/fonts/roboto/roboto-regular.woff2',\n        // ... any other fonts ...\n        // JS\n        '/static/js/yourapp.min.js'\n        // ... any other JS - as with the other entries, the order you specify here doesn't matter,\n        // the files will be loaded in the order you indicate in your HTML document. ...\n    ];\n\n/**\n * On install of this worker, add all cacheableResources to the staticCache.\n * Note that workers will be (re-)installed when they have changed (byte-wise comparison)\n * from the last worker encountered with the registered url (see the installation of workers, above),\n * which can be as simple as changing the cacheVersion number to point to a new 'version' of the cache.\n * You will want to update that cacheVersion number each time you change any of the cached resources.\n * Doing so will cause the worker to re-request and re-cache the cacheableResources, which is how we\n * will refresh cached resources for the application.\n */\nself.addEventListener('install', function(event){\n    event.waitUntil(\n        caches.open(staticCache).then(function(cache){\n            return cache.addAll(cacheableResources);\n        }).then(function(){\n            // Take control of the client as soon as we're installed\n            // and the cache has been updated.\n            return self.skipWaiting();\n        })\n    );\n});\n\n/**\n * On activation of this service worker, delete old caches. Note that\n * we return a promise that resolves when all promises returned by\n * the delete calls within it resolve.\n * Once we've deleted the old cache, we need to let the clients know that\n * a new service worker (with a new cache) has taken over, and they'll\n * need to reload in order to get the newly cached resources, via\n * postMessage.\n * @param event\n */\nself.addEventListener('activate', function(event){\n    event.waitUntil(\n        caches.keys().then(function(cacheNames){\n            return Promise.all(\n                cacheNames.map(function(cacheName){\n                    if (cacheName !== staticCache){\n                        return caches.delete(cacheName);\n                    }\n                })\n            );\n        }).then(function(){\n            return self.clients.matchAll().then(function(clients){\n                return Promise.all(clients.map(function(client){\n                    return client.postMessage({message:'needs-reload'});\n                }));\n            });\n        })\n    );\n});\n\n/**\n * Intercept network requests so that we can serve the requested resource from the\n * cache, if we have it, or otherwise defer to the network.\n */\nself.addEventListener('fetch', function(event){\n    // Workaround for Chromium bug that makes ignoring the search\n    // parameter very slow when matching the request against the\n    // cached values: https://bugs.chromium.org/p/chromium/issues/detail?id=682677.\n    // Your application may not need this - or hey, it may even be fixed by the time\n    // you're reading this!\n    var hasSearch = (event.request.url.indexOf('?') !== -1);\n\n    event.respondWith(\n        caches.match(event.request, {\n            ignoreSearch: hasSearch\n        }).then(function(response){\n            return response || fetch(event.request);\n        })\n    );\n});\n",[3314],{"type":20,"tag":94,"props":3315,"children":3316},{"__ignoreMap":8},[3317,3324,3332,3340,3358,3365,3391,3417,3433,3441,3453,3461,3473,3481,3489,3501,3509,3517,3529,3537,3545,3553,3561,3569,3577,3584,3591,3599,3607,3615,3623,3631,3639,3647,3654,3695,3713,3756,3778,3802,3810,3818,3840,3848,3856,3864,3871,3878,3886,3894,3902,3910,3918,3926,3934,3949,3956,3996,4012,4055,4081,4116,4140,4164,4173,4182,4191,4215,4262,4313,4346,4355,4364,4372,4380,4388,4396,4404,4413,4422,4430,4471,4480,4489,4498,4507,4516,4575,4583,4600,4618,4627,4660,4688,4696,4704],{"type":20,"tag":98,"props":3318,"children":3319},{"class":100,"line":101},[3320],{"type":20,"tag":98,"props":3321,"children":3322},{"style":686},[3323],{"type":30,"value":2430},{"type":20,"tag":98,"props":3325,"children":3326},{"class":100,"line":111},[3327],{"type":20,"tag":98,"props":3328,"children":3329},{"style":686},[3330],{"type":30,"value":3331}," * Service worker intended for caching and serving files when the application is offline,\n",{"type":20,"tag":98,"props":3333,"children":3334},{"class":100,"line":137},[3335],{"type":20,"tag":98,"props":3336,"children":3337},{"style":686},[3338],{"type":30,"value":3339}," * to meet the requirements for a PWA.\n",{"type":20,"tag":98,"props":3341,"children":3342},{"class":100,"line":159},[3343,3348,3353],{"type":20,"tag":98,"props":3344,"children":3345},{"style":686},[3346],{"type":30,"value":3347}," * ",{"type":20,"tag":98,"props":3349,"children":3350},{"style":1908},[3351],{"type":30,"value":3352},"@author",{"type":20,"tag":98,"props":3354,"children":3355},{"style":706},[3356],{"type":30,"value":3357}," Christopher Keefer\n",{"type":20,"tag":98,"props":3359,"children":3360},{"class":100,"line":181},[3361],{"type":20,"tag":98,"props":3362,"children":3363},{"style":686},[3364],{"type":30,"value":2446},{"type":20,"tag":98,"props":3366,"children":3367},{"class":100,"line":202},[3368,3373,3378,3382,3387],{"type":20,"tag":98,"props":3369,"children":3370},{"style":1908},[3371],{"type":30,"value":3372},"var",{"type":20,"tag":98,"props":3374,"children":3375},{"style":105},[3376],{"type":30,"value":3377}," cacheVersion ",{"type":20,"tag":98,"props":3379,"children":3380},{"style":1908},[3381],{"type":30,"value":714},{"type":20,"tag":98,"props":3383,"children":3384},{"style":115},[3385],{"type":30,"value":3386}," 1",{"type":20,"tag":98,"props":3388,"children":3389},{"style":105},[3390],{"type":30,"value":134},{"type":20,"tag":98,"props":3392,"children":3393},{"class":100,"line":224},[3394,3399,3403,3408,3412],{"type":20,"tag":98,"props":3395,"children":3396},{"style":105},[3397],{"type":30,"value":3398},"    staticCache ",{"type":20,"tag":98,"props":3400,"children":3401},{"style":1908},[3402],{"type":30,"value":714},{"type":20,"tag":98,"props":3404,"children":3405},{"style":126},[3406],{"type":30,"value":3407}," 'static-cache-v'",{"type":20,"tag":98,"props":3409,"children":3410},{"style":1908},[3411],{"type":30,"value":2304},{"type":20,"tag":98,"props":3413,"children":3414},{"style":105},[3415],{"type":30,"value":3416},"cacheVersion,\n",{"type":20,"tag":98,"props":3418,"children":3419},{"class":100,"line":246},[3420,3425,3429],{"type":20,"tag":98,"props":3421,"children":3422},{"style":105},[3423],{"type":30,"value":3424},"    cacheableResources ",{"type":20,"tag":98,"props":3426,"children":3427},{"style":1908},[3428],{"type":30,"value":714},{"type":20,"tag":98,"props":3430,"children":3431},{"style":105},[3432],{"type":30,"value":2236},{"type":20,"tag":98,"props":3434,"children":3435},{"class":100,"line":260},[3436],{"type":20,"tag":98,"props":3437,"children":3438},{"style":686},[3439],{"type":30,"value":3440},"        // Root - This MUST be in the cacheable resources for a PWA!\n",{"type":20,"tag":98,"props":3442,"children":3443},{"class":100,"line":269},[3444,3449],{"type":20,"tag":98,"props":3445,"children":3446},{"style":126},[3447],{"type":30,"value":3448},"        '/'",{"type":20,"tag":98,"props":3450,"children":3451},{"style":105},[3452],{"type":30,"value":134},{"type":20,"tag":98,"props":3454,"children":3455},{"class":100,"line":291},[3456],{"type":20,"tag":98,"props":3457,"children":3458},{"style":686},[3459],{"type":30,"value":3460},"        // Images\n",{"type":20,"tag":98,"props":3462,"children":3463},{"class":100,"line":313},[3464,3469],{"type":20,"tag":98,"props":3465,"children":3466},{"style":126},[3467],{"type":30,"value":3468},"        '/static/img/yourLogo.png'",{"type":20,"tag":98,"props":3470,"children":3471},{"style":105},[3472],{"type":30,"value":134},{"type":20,"tag":98,"props":3474,"children":3475},{"class":100,"line":331},[3476],{"type":20,"tag":98,"props":3477,"children":3478},{"style":686},[3479],{"type":30,"value":3480},"        //... any other static image resources your application will need ...\n",{"type":20,"tag":98,"props":3482,"children":3483},{"class":100,"line":340},[3484],{"type":20,"tag":98,"props":3485,"children":3486},{"style":686},[3487],{"type":30,"value":3488},"        // CSS\n",{"type":20,"tag":98,"props":3490,"children":3491},{"class":100,"line":348},[3492,3497],{"type":20,"tag":98,"props":3493,"children":3494},{"style":126},[3495],{"type":30,"value":3496},"        '/static/css/yourapp.min.css'",{"type":20,"tag":98,"props":3498,"children":3499},{"style":105},[3500],{"type":30,"value":134},{"type":20,"tag":98,"props":3502,"children":3503},{"class":100,"line":369},[3504],{"type":20,"tag":98,"props":3505,"children":3506},{"style":686},[3507],{"type":30,"value":3508},"        // ... any other styling your app will need, order doesn't matter ....\n",{"type":20,"tag":98,"props":3510,"children":3511},{"class":100,"line":389},[3512],{"type":20,"tag":98,"props":3513,"children":3514},{"style":686},[3515],{"type":30,"value":3516},"        // Fonts\n",{"type":20,"tag":98,"props":3518,"children":3519},{"class":100,"line":406},[3520,3525],{"type":20,"tag":98,"props":3521,"children":3522},{"style":126},[3523],{"type":30,"value":3524},"        '/static/css/fonts/roboto/roboto-regular.woff2'",{"type":20,"tag":98,"props":3526,"children":3527},{"style":105},[3528],{"type":30,"value":134},{"type":20,"tag":98,"props":3530,"children":3531},{"class":100,"line":414},[3532],{"type":20,"tag":98,"props":3533,"children":3534},{"style":686},[3535],{"type":30,"value":3536},"        // ... any other fonts ...\n",{"type":20,"tag":98,"props":3538,"children":3539},{"class":100,"line":422},[3540],{"type":20,"tag":98,"props":3541,"children":3542},{"style":686},[3543],{"type":30,"value":3544},"        // JS\n",{"type":20,"tag":98,"props":3546,"children":3547},{"class":100,"line":443},[3548],{"type":20,"tag":98,"props":3549,"children":3550},{"style":126},[3551],{"type":30,"value":3552},"        '/static/js/yourapp.min.js'\n",{"type":20,"tag":98,"props":3554,"children":3555},{"class":100,"line":463},[3556],{"type":20,"tag":98,"props":3557,"children":3558},{"style":686},[3559],{"type":30,"value":3560},"        // ... any other JS - as with the other entries, the order you specify here doesn't matter,\n",{"type":20,"tag":98,"props":3562,"children":3563},{"class":100,"line":480},[3564],{"type":20,"tag":98,"props":3565,"children":3566},{"style":686},[3567],{"type":30,"value":3568},"        // the files will be loaded in the order you indicate in your HTML document. ...\n",{"type":20,"tag":98,"props":3570,"children":3571},{"class":100,"line":489},[3572],{"type":20,"tag":98,"props":3573,"children":3574},{"style":105},[3575],{"type":30,"value":3576},"    ];\n",{"type":20,"tag":98,"props":3578,"children":3579},{"class":100,"line":498},[3580],{"type":20,"tag":98,"props":3581,"children":3582},{"emptyLinePlaceholder":989},[3583],{"type":30,"value":992},{"type":20,"tag":98,"props":3585,"children":3586},{"class":100,"line":516},[3587],{"type":20,"tag":98,"props":3588,"children":3589},{"style":686},[3590],{"type":30,"value":2430},{"type":20,"tag":98,"props":3592,"children":3593},{"class":100,"line":1273},[3594],{"type":20,"tag":98,"props":3595,"children":3596},{"style":686},[3597],{"type":30,"value":3598}," * On install of this worker, add all cacheableResources to the staticCache.\n",{"type":20,"tag":98,"props":3600,"children":3601},{"class":100,"line":1289},[3602],{"type":20,"tag":98,"props":3603,"children":3604},{"style":686},[3605],{"type":30,"value":3606}," * Note that workers will be (re-)installed when they have changed (byte-wise comparison)\n",{"type":20,"tag":98,"props":3608,"children":3609},{"class":100,"line":1297},[3610],{"type":20,"tag":98,"props":3611,"children":3612},{"style":686},[3613],{"type":30,"value":3614}," * from the last worker encountered with the registered url (see the installation of workers, above),\n",{"type":20,"tag":98,"props":3616,"children":3617},{"class":100,"line":1317},[3618],{"type":20,"tag":98,"props":3619,"children":3620},{"style":686},[3621],{"type":30,"value":3622}," * which can be as simple as changing the cacheVersion number to point to a new 'version' of the cache.\n",{"type":20,"tag":98,"props":3624,"children":3625},{"class":100,"line":1325},[3626],{"type":20,"tag":98,"props":3627,"children":3628},{"style":686},[3629],{"type":30,"value":3630}," * You will want to update that cacheVersion number each time you change any of the cached resources.\n",{"type":20,"tag":98,"props":3632,"children":3633},{"class":100,"line":1334},[3634],{"type":20,"tag":98,"props":3635,"children":3636},{"style":686},[3637],{"type":30,"value":3638}," * Doing so will cause the worker to re-request and re-cache the cacheableResources, which is how we\n",{"type":20,"tag":98,"props":3640,"children":3641},{"class":100,"line":1359},[3642],{"type":20,"tag":98,"props":3643,"children":3644},{"style":686},[3645],{"type":30,"value":3646}," * will refresh cached resources for the application.\n",{"type":20,"tag":98,"props":3648,"children":3649},{"class":100,"line":1376},[3650],{"type":20,"tag":98,"props":3651,"children":3652},{"style":686},[3653],{"type":30,"value":2446},{"type":20,"tag":98,"props":3655,"children":3656},{"class":100,"line":1384},[3657,3662,3666,3670,3675,3679,3683,3687,3691],{"type":20,"tag":98,"props":3658,"children":3659},{"style":105},[3660],{"type":30,"value":3661},"self.",{"type":20,"tag":98,"props":3663,"children":3664},{"style":706},[3665],{"type":30,"value":2981},{"type":20,"tag":98,"props":3667,"children":3668},{"style":105},[3669],{"type":30,"value":2278},{"type":20,"tag":98,"props":3671,"children":3672},{"style":126},[3673],{"type":30,"value":3674},"'install'",{"type":20,"tag":98,"props":3676,"children":3677},{"style":105},[3678],{"type":30,"value":2064},{"type":20,"tag":98,"props":3680,"children":3681},{"style":1908},[3682],{"type":30,"value":2458},{"type":20,"tag":98,"props":3684,"children":3685},{"style":105},[3686],{"type":30,"value":2278},{"type":20,"tag":98,"props":3688,"children":3689},{"style":2160},[3690],{"type":30,"value":2881},{"type":20,"tag":98,"props":3692,"children":3693},{"style":105},[3694],{"type":30,"value":2472},{"type":20,"tag":98,"props":3696,"children":3697},{"class":100,"line":3005},[3698,3703,3708],{"type":20,"tag":98,"props":3699,"children":3700},{"style":105},[3701],{"type":30,"value":3702},"    event.",{"type":20,"tag":98,"props":3704,"children":3705},{"style":706},[3706],{"type":30,"value":3707},"waitUntil",{"type":20,"tag":98,"props":3709,"children":3710},{"style":105},[3711],{"type":30,"value":3712},"(\n",{"type":20,"tag":98,"props":3714,"children":3715},{"class":100,"line":3014},[3716,3721,3726,3731,3735,3739,3743,3747,3752],{"type":20,"tag":98,"props":3717,"children":3718},{"style":105},[3719],{"type":30,"value":3720},"        caches.",{"type":20,"tag":98,"props":3722,"children":3723},{"style":706},[3724],{"type":30,"value":3725},"open",{"type":20,"tag":98,"props":3727,"children":3728},{"style":105},[3729],{"type":30,"value":3730},"(staticCache).",{"type":20,"tag":98,"props":3732,"children":3733},{"style":706},[3734],{"type":30,"value":2679},{"type":20,"tag":98,"props":3736,"children":3737},{"style":105},[3738],{"type":30,"value":2278},{"type":20,"tag":98,"props":3740,"children":3741},{"style":1908},[3742],{"type":30,"value":2458},{"type":20,"tag":98,"props":3744,"children":3745},{"style":105},[3746],{"type":30,"value":2278},{"type":20,"tag":98,"props":3748,"children":3749},{"style":2160},[3750],{"type":30,"value":3751},"cache",{"type":20,"tag":98,"props":3753,"children":3754},{"style":105},[3755],{"type":30,"value":2472},{"type":20,"tag":98,"props":3757,"children":3758},{"class":100,"line":3023},[3759,3763,3768,3773],{"type":20,"tag":98,"props":3760,"children":3761},{"style":1908},[3762],{"type":30,"value":2152},{"type":20,"tag":98,"props":3764,"children":3765},{"style":105},[3766],{"type":30,"value":3767}," cache.",{"type":20,"tag":98,"props":3769,"children":3770},{"style":706},[3771],{"type":30,"value":3772},"addAll",{"type":20,"tag":98,"props":3774,"children":3775},{"style":105},[3776],{"type":30,"value":3777},"(cacheableResources);\n",{"type":20,"tag":98,"props":3779,"children":3780},{"class":100,"line":3032},[3781,3786,3790,3794,3798],{"type":20,"tag":98,"props":3782,"children":3783},{"style":105},[3784],{"type":30,"value":3785},"        }).",{"type":20,"tag":98,"props":3787,"children":3788},{"style":706},[3789],{"type":30,"value":2679},{"type":20,"tag":98,"props":3791,"children":3792},{"style":105},[3793],{"type":30,"value":2278},{"type":20,"tag":98,"props":3795,"children":3796},{"style":1908},[3797],{"type":30,"value":2458},{"type":20,"tag":98,"props":3799,"children":3800},{"style":105},[3801],{"type":30,"value":3173},{"type":20,"tag":98,"props":3803,"children":3804},{"class":100,"line":3041},[3805],{"type":20,"tag":98,"props":3806,"children":3807},{"style":686},[3808],{"type":30,"value":3809},"            // Take control of the client as soon as we're installed\n",{"type":20,"tag":98,"props":3811,"children":3812},{"class":100,"line":3050},[3813],{"type":20,"tag":98,"props":3814,"children":3815},{"style":686},[3816],{"type":30,"value":3817},"            // and the cache has been updated.\n",{"type":20,"tag":98,"props":3819,"children":3820},{"class":100,"line":3059},[3821,3825,3830,3835],{"type":20,"tag":98,"props":3822,"children":3823},{"style":1908},[3824],{"type":30,"value":2152},{"type":20,"tag":98,"props":3826,"children":3827},{"style":105},[3828],{"type":30,"value":3829}," self.",{"type":20,"tag":98,"props":3831,"children":3832},{"style":706},[3833],{"type":30,"value":3834},"skipWaiting",{"type":20,"tag":98,"props":3836,"children":3837},{"style":105},[3838],{"type":30,"value":3839},"();\n",{"type":20,"tag":98,"props":3841,"children":3842},{"class":100,"line":3068},[3843],{"type":20,"tag":98,"props":3844,"children":3845},{"style":105},[3846],{"type":30,"value":3847},"        })\n",{"type":20,"tag":98,"props":3849,"children":3850},{"class":100,"line":3077},[3851],{"type":20,"tag":98,"props":3852,"children":3853},{"style":105},[3854],{"type":30,"value":3855},"    );\n",{"type":20,"tag":98,"props":3857,"children":3858},{"class":100,"line":3086},[3859],{"type":20,"tag":98,"props":3860,"children":3861},{"style":105},[3862],{"type":30,"value":3863},"});\n",{"type":20,"tag":98,"props":3865,"children":3866},{"class":100,"line":3095},[3867],{"type":20,"tag":98,"props":3868,"children":3869},{"emptyLinePlaceholder":989},[3870],{"type":30,"value":992},{"type":20,"tag":98,"props":3872,"children":3873},{"class":100,"line":3104},[3874],{"type":20,"tag":98,"props":3875,"children":3876},{"style":686},[3877],{"type":30,"value":2430},{"type":20,"tag":98,"props":3879,"children":3880},{"class":100,"line":3133},[3881],{"type":20,"tag":98,"props":3882,"children":3883},{"style":686},[3884],{"type":30,"value":3885}," * On activation of this service worker, delete old caches. Note that\n",{"type":20,"tag":98,"props":3887,"children":3888},{"class":100,"line":3141},[3889],{"type":20,"tag":98,"props":3890,"children":3891},{"style":686},[3892],{"type":30,"value":3893}," * we return a promise that resolves when all promises returned by\n",{"type":20,"tag":98,"props":3895,"children":3896},{"class":100,"line":3176},[3897],{"type":20,"tag":98,"props":3898,"children":3899},{"style":686},[3900],{"type":30,"value":3901}," * the delete calls within it resolve.\n",{"type":20,"tag":98,"props":3903,"children":3904},{"class":100,"line":3202},[3905],{"type":20,"tag":98,"props":3906,"children":3907},{"style":686},[3908],{"type":30,"value":3909}," * Once we've deleted the old cache, we need to let the clients know that\n",{"type":20,"tag":98,"props":3911,"children":3912},{"class":100,"line":3210},[3913],{"type":20,"tag":98,"props":3914,"children":3915},{"style":686},[3916],{"type":30,"value":3917}," * a new service worker (with a new cache) has taken over, and they'll\n",{"type":20,"tag":98,"props":3919,"children":3920},{"class":100,"line":3218},[3921],{"type":20,"tag":98,"props":3922,"children":3923},{"style":686},[3924],{"type":30,"value":3925}," * need to reload in order to get the newly cached resources, via\n",{"type":20,"tag":98,"props":3927,"children":3928},{"class":100,"line":3251},[3929],{"type":20,"tag":98,"props":3930,"children":3931},{"style":686},[3932],{"type":30,"value":3933}," * postMessage.\n",{"type":20,"tag":98,"props":3935,"children":3936},{"class":100,"line":3276},[3937,3941,3945],{"type":20,"tag":98,"props":3938,"children":3939},{"style":686},[3940],{"type":30,"value":3347},{"type":20,"tag":98,"props":3942,"children":3943},{"style":1908},[3944],{"type":30,"value":2587},{"type":20,"tag":98,"props":3946,"children":3947},{"style":105},[3948],{"type":30,"value":2853},{"type":20,"tag":98,"props":3950,"children":3951},{"class":100,"line":3284},[3952],{"type":20,"tag":98,"props":3953,"children":3954},{"style":686},[3955],{"type":30,"value":2446},{"type":20,"tag":98,"props":3957,"children":3958},{"class":100,"line":3292},[3959,3963,3967,3971,3976,3980,3984,3988,3992],{"type":20,"tag":98,"props":3960,"children":3961},{"style":105},[3962],{"type":30,"value":3661},{"type":20,"tag":98,"props":3964,"children":3965},{"style":706},[3966],{"type":30,"value":2981},{"type":20,"tag":98,"props":3968,"children":3969},{"style":105},[3970],{"type":30,"value":2278},{"type":20,"tag":98,"props":3972,"children":3973},{"style":126},[3974],{"type":30,"value":3975},"'activate'",{"type":20,"tag":98,"props":3977,"children":3978},{"style":105},[3979],{"type":30,"value":2064},{"type":20,"tag":98,"props":3981,"children":3982},{"style":1908},[3983],{"type":30,"value":2458},{"type":20,"tag":98,"props":3985,"children":3986},{"style":105},[3987],{"type":30,"value":2278},{"type":20,"tag":98,"props":3989,"children":3990},{"style":2160},[3991],{"type":30,"value":2881},{"type":20,"tag":98,"props":3993,"children":3994},{"style":105},[3995],{"type":30,"value":2472},{"type":20,"tag":98,"props":3997,"children":3999},{"class":100,"line":3998},58,[4000,4004,4008],{"type":20,"tag":98,"props":4001,"children":4002},{"style":105},[4003],{"type":30,"value":3702},{"type":20,"tag":98,"props":4005,"children":4006},{"style":706},[4007],{"type":30,"value":3707},{"type":20,"tag":98,"props":4009,"children":4010},{"style":105},[4011],{"type":30,"value":3712},{"type":20,"tag":98,"props":4013,"children":4015},{"class":100,"line":4014},59,[4016,4020,4025,4030,4034,4038,4042,4046,4051],{"type":20,"tag":98,"props":4017,"children":4018},{"style":105},[4019],{"type":30,"value":3720},{"type":20,"tag":98,"props":4021,"children":4022},{"style":706},[4023],{"type":30,"value":4024},"keys",{"type":20,"tag":98,"props":4026,"children":4027},{"style":105},[4028],{"type":30,"value":4029},"().",{"type":20,"tag":98,"props":4031,"children":4032},{"style":706},[4033],{"type":30,"value":2679},{"type":20,"tag":98,"props":4035,"children":4036},{"style":105},[4037],{"type":30,"value":2278},{"type":20,"tag":98,"props":4039,"children":4040},{"style":1908},[4041],{"type":30,"value":2458},{"type":20,"tag":98,"props":4043,"children":4044},{"style":105},[4045],{"type":30,"value":2278},{"type":20,"tag":98,"props":4047,"children":4048},{"style":2160},[4049],{"type":30,"value":4050},"cacheNames",{"type":20,"tag":98,"props":4052,"children":4053},{"style":105},[4054],{"type":30,"value":2472},{"type":20,"tag":98,"props":4056,"children":4058},{"class":100,"line":4057},60,[4059,4063,4068,4072,4077],{"type":20,"tag":98,"props":4060,"children":4061},{"style":1908},[4062],{"type":30,"value":2152},{"type":20,"tag":98,"props":4064,"children":4065},{"style":115},[4066],{"type":30,"value":4067}," Promise",{"type":20,"tag":98,"props":4069,"children":4070},{"style":105},[4071],{"type":30,"value":3120},{"type":20,"tag":98,"props":4073,"children":4074},{"style":706},[4075],{"type":30,"value":4076},"all",{"type":20,"tag":98,"props":4078,"children":4079},{"style":105},[4080],{"type":30,"value":3712},{"type":20,"tag":98,"props":4082,"children":4084},{"class":100,"line":4083},61,[4085,4090,4095,4099,4103,4107,4112],{"type":20,"tag":98,"props":4086,"children":4087},{"style":105},[4088],{"type":30,"value":4089},"                cacheNames.",{"type":20,"tag":98,"props":4091,"children":4092},{"style":706},[4093],{"type":30,"value":4094},"map",{"type":20,"tag":98,"props":4096,"children":4097},{"style":105},[4098],{"type":30,"value":2278},{"type":20,"tag":98,"props":4100,"children":4101},{"style":1908},[4102],{"type":30,"value":2458},{"type":20,"tag":98,"props":4104,"children":4105},{"style":105},[4106],{"type":30,"value":2278},{"type":20,"tag":98,"props":4108,"children":4109},{"style":2160},[4110],{"type":30,"value":4111},"cacheName",{"type":20,"tag":98,"props":4113,"children":4114},{"style":105},[4115],{"type":30,"value":2472},{"type":20,"tag":98,"props":4117,"children":4119},{"class":100,"line":4118},62,[4120,4125,4130,4135],{"type":20,"tag":98,"props":4121,"children":4122},{"style":1908},[4123],{"type":30,"value":4124},"                    if",{"type":20,"tag":98,"props":4126,"children":4127},{"style":105},[4128],{"type":30,"value":4129}," (cacheName ",{"type":20,"tag":98,"props":4131,"children":4132},{"style":1908},[4133],{"type":30,"value":4134},"!==",{"type":20,"tag":98,"props":4136,"children":4137},{"style":105},[4138],{"type":30,"value":4139}," staticCache){\n",{"type":20,"tag":98,"props":4141,"children":4143},{"class":100,"line":4142},63,[4144,4149,4154,4159],{"type":20,"tag":98,"props":4145,"children":4146},{"style":1908},[4147],{"type":30,"value":4148},"                        return",{"type":20,"tag":98,"props":4150,"children":4151},{"style":105},[4152],{"type":30,"value":4153}," caches.",{"type":20,"tag":98,"props":4155,"children":4156},{"style":706},[4157],{"type":30,"value":4158},"delete",{"type":20,"tag":98,"props":4160,"children":4161},{"style":105},[4162],{"type":30,"value":4163},"(cacheName);\n",{"type":20,"tag":98,"props":4165,"children":4167},{"class":100,"line":4166},64,[4168],{"type":20,"tag":98,"props":4169,"children":4170},{"style":105},[4171],{"type":30,"value":4172},"                    }\n",{"type":20,"tag":98,"props":4174,"children":4176},{"class":100,"line":4175},65,[4177],{"type":20,"tag":98,"props":4178,"children":4179},{"style":105},[4180],{"type":30,"value":4181},"                })\n",{"type":20,"tag":98,"props":4183,"children":4185},{"class":100,"line":4184},66,[4186],{"type":20,"tag":98,"props":4187,"children":4188},{"style":105},[4189],{"type":30,"value":4190},"            );\n",{"type":20,"tag":98,"props":4192,"children":4194},{"class":100,"line":4193},67,[4195,4199,4203,4207,4211],{"type":20,"tag":98,"props":4196,"children":4197},{"style":105},[4198],{"type":30,"value":3785},{"type":20,"tag":98,"props":4200,"children":4201},{"style":706},[4202],{"type":30,"value":2679},{"type":20,"tag":98,"props":4204,"children":4205},{"style":105},[4206],{"type":30,"value":2278},{"type":20,"tag":98,"props":4208,"children":4209},{"style":1908},[4210],{"type":30,"value":2458},{"type":20,"tag":98,"props":4212,"children":4213},{"style":105},[4214],{"type":30,"value":3173},{"type":20,"tag":98,"props":4216,"children":4218},{"class":100,"line":4217},68,[4219,4223,4228,4233,4237,4241,4245,4249,4253,4258],{"type":20,"tag":98,"props":4220,"children":4221},{"style":1908},[4222],{"type":30,"value":2152},{"type":20,"tag":98,"props":4224,"children":4225},{"style":105},[4226],{"type":30,"value":4227}," self.clients.",{"type":20,"tag":98,"props":4229,"children":4230},{"style":706},[4231],{"type":30,"value":4232},"matchAll",{"type":20,"tag":98,"props":4234,"children":4235},{"style":105},[4236],{"type":30,"value":4029},{"type":20,"tag":98,"props":4238,"children":4239},{"style":706},[4240],{"type":30,"value":2679},{"type":20,"tag":98,"props":4242,"children":4243},{"style":105},[4244],{"type":30,"value":2278},{"type":20,"tag":98,"props":4246,"children":4247},{"style":1908},[4248],{"type":30,"value":2458},{"type":20,"tag":98,"props":4250,"children":4251},{"style":105},[4252],{"type":30,"value":2278},{"type":20,"tag":98,"props":4254,"children":4255},{"style":2160},[4256],{"type":30,"value":4257},"clients",{"type":20,"tag":98,"props":4259,"children":4260},{"style":105},[4261],{"type":30,"value":2472},{"type":20,"tag":98,"props":4263,"children":4265},{"class":100,"line":4264},69,[4266,4271,4275,4279,4283,4288,4292,4296,4300,4304,4309],{"type":20,"tag":98,"props":4267,"children":4268},{"style":1908},[4269],{"type":30,"value":4270},"                return",{"type":20,"tag":98,"props":4272,"children":4273},{"style":115},[4274],{"type":30,"value":4067},{"type":20,"tag":98,"props":4276,"children":4277},{"style":105},[4278],{"type":30,"value":3120},{"type":20,"tag":98,"props":4280,"children":4281},{"style":706},[4282],{"type":30,"value":4076},{"type":20,"tag":98,"props":4284,"children":4285},{"style":105},[4286],{"type":30,"value":4287},"(clients.",{"type":20,"tag":98,"props":4289,"children":4290},{"style":706},[4291],{"type":30,"value":4094},{"type":20,"tag":98,"props":4293,"children":4294},{"style":105},[4295],{"type":30,"value":2278},{"type":20,"tag":98,"props":4297,"children":4298},{"style":1908},[4299],{"type":30,"value":2458},{"type":20,"tag":98,"props":4301,"children":4302},{"style":105},[4303],{"type":30,"value":2278},{"type":20,"tag":98,"props":4305,"children":4306},{"style":2160},[4307],{"type":30,"value":4308},"client",{"type":20,"tag":98,"props":4310,"children":4311},{"style":105},[4312],{"type":30,"value":2472},{"type":20,"tag":98,"props":4314,"children":4316},{"class":100,"line":4315},70,[4317,4322,4327,4332,4337,4342],{"type":20,"tag":98,"props":4318,"children":4319},{"style":1908},[4320],{"type":30,"value":4321},"                    return",{"type":20,"tag":98,"props":4323,"children":4324},{"style":105},[4325],{"type":30,"value":4326}," client.",{"type":20,"tag":98,"props":4328,"children":4329},{"style":706},[4330],{"type":30,"value":4331},"postMessage",{"type":20,"tag":98,"props":4333,"children":4334},{"style":105},[4335],{"type":30,"value":4336},"({message:",{"type":20,"tag":98,"props":4338,"children":4339},{"style":126},[4340],{"type":30,"value":4341},"'needs-reload'",{"type":20,"tag":98,"props":4343,"children":4344},{"style":105},[4345],{"type":30,"value":3863},{"type":20,"tag":98,"props":4347,"children":4349},{"class":100,"line":4348},71,[4350],{"type":20,"tag":98,"props":4351,"children":4352},{"style":105},[4353],{"type":30,"value":4354},"                }));\n",{"type":20,"tag":98,"props":4356,"children":4358},{"class":100,"line":4357},72,[4359],{"type":20,"tag":98,"props":4360,"children":4361},{"style":105},[4362],{"type":30,"value":4363},"            });\n",{"type":20,"tag":98,"props":4365,"children":4367},{"class":100,"line":4366},73,[4368],{"type":20,"tag":98,"props":4369,"children":4370},{"style":105},[4371],{"type":30,"value":3847},{"type":20,"tag":98,"props":4373,"children":4375},{"class":100,"line":4374},74,[4376],{"type":20,"tag":98,"props":4377,"children":4378},{"style":105},[4379],{"type":30,"value":3855},{"type":20,"tag":98,"props":4381,"children":4383},{"class":100,"line":4382},75,[4384],{"type":20,"tag":98,"props":4385,"children":4386},{"style":105},[4387],{"type":30,"value":3863},{"type":20,"tag":98,"props":4389,"children":4391},{"class":100,"line":4390},76,[4392],{"type":20,"tag":98,"props":4393,"children":4394},{"emptyLinePlaceholder":989},[4395],{"type":30,"value":992},{"type":20,"tag":98,"props":4397,"children":4399},{"class":100,"line":4398},77,[4400],{"type":20,"tag":98,"props":4401,"children":4402},{"style":686},[4403],{"type":30,"value":2430},{"type":20,"tag":98,"props":4405,"children":4407},{"class":100,"line":4406},78,[4408],{"type":20,"tag":98,"props":4409,"children":4410},{"style":686},[4411],{"type":30,"value":4412}," * Intercept network requests so that we can serve the requested resource from the\n",{"type":20,"tag":98,"props":4414,"children":4416},{"class":100,"line":4415},79,[4417],{"type":20,"tag":98,"props":4418,"children":4419},{"style":686},[4420],{"type":30,"value":4421}," * cache, if we have it, or otherwise defer to the network.\n",{"type":20,"tag":98,"props":4423,"children":4425},{"class":100,"line":4424},80,[4426],{"type":20,"tag":98,"props":4427,"children":4428},{"style":686},[4429],{"type":30,"value":2446},{"type":20,"tag":98,"props":4431,"children":4433},{"class":100,"line":4432},81,[4434,4438,4442,4446,4451,4455,4459,4463,4467],{"type":20,"tag":98,"props":4435,"children":4436},{"style":105},[4437],{"type":30,"value":3661},{"type":20,"tag":98,"props":4439,"children":4440},{"style":706},[4441],{"type":30,"value":2981},{"type":20,"tag":98,"props":4443,"children":4444},{"style":105},[4445],{"type":30,"value":2278},{"type":20,"tag":98,"props":4447,"children":4448},{"style":126},[4449],{"type":30,"value":4450},"'fetch'",{"type":20,"tag":98,"props":4452,"children":4453},{"style":105},[4454],{"type":30,"value":2064},{"type":20,"tag":98,"props":4456,"children":4457},{"style":1908},[4458],{"type":30,"value":2458},{"type":20,"tag":98,"props":4460,"children":4461},{"style":105},[4462],{"type":30,"value":2278},{"type":20,"tag":98,"props":4464,"children":4465},{"style":2160},[4466],{"type":30,"value":2881},{"type":20,"tag":98,"props":4468,"children":4469},{"style":105},[4470],{"type":30,"value":2472},{"type":20,"tag":98,"props":4472,"children":4474},{"class":100,"line":4473},82,[4475],{"type":20,"tag":98,"props":4476,"children":4477},{"style":686},[4478],{"type":30,"value":4479},"    // Workaround for Chromium bug that makes ignoring the search\n",{"type":20,"tag":98,"props":4481,"children":4483},{"class":100,"line":4482},83,[4484],{"type":20,"tag":98,"props":4485,"children":4486},{"style":686},[4487],{"type":30,"value":4488},"    // parameter very slow when matching the request against the\n",{"type":20,"tag":98,"props":4490,"children":4492},{"class":100,"line":4491},84,[4493],{"type":20,"tag":98,"props":4494,"children":4495},{"style":686},[4496],{"type":30,"value":4497},"    // cached values: https://bugs.chromium.org/p/chromium/issues/detail?id=682677.\n",{"type":20,"tag":98,"props":4499,"children":4501},{"class":100,"line":4500},85,[4502],{"type":20,"tag":98,"props":4503,"children":4504},{"style":686},[4505],{"type":30,"value":4506},"    // Your application may not need this - or hey, it may even be fixed by the time\n",{"type":20,"tag":98,"props":4508,"children":4510},{"class":100,"line":4509},86,[4511],{"type":20,"tag":98,"props":4512,"children":4513},{"style":686},[4514],{"type":30,"value":4515},"    // you're reading this!\n",{"type":20,"tag":98,"props":4517,"children":4519},{"class":100,"line":4518},87,[4520,4524,4529,4533,4538,4543,4547,4552,4556,4560,4565,4570],{"type":20,"tag":98,"props":4521,"children":4522},{"style":1908},[4523],{"type":30,"value":2480},{"type":20,"tag":98,"props":4525,"children":4526},{"style":105},[4527],{"type":30,"value":4528}," hasSearch ",{"type":20,"tag":98,"props":4530,"children":4531},{"style":1908},[4532],{"type":30,"value":714},{"type":20,"tag":98,"props":4534,"children":4535},{"style":105},[4536],{"type":30,"value":4537}," (event.request.url.",{"type":20,"tag":98,"props":4539,"children":4540},{"style":706},[4541],{"type":30,"value":4542},"indexOf",{"type":20,"tag":98,"props":4544,"children":4545},{"style":105},[4546],{"type":30,"value":2278},{"type":20,"tag":98,"props":4548,"children":4549},{"style":126},[4550],{"type":30,"value":4551},"'?'",{"type":20,"tag":98,"props":4553,"children":4554},{"style":105},[4555],{"type":30,"value":2134},{"type":20,"tag":98,"props":4557,"children":4558},{"style":1908},[4559],{"type":30,"value":4134},{"type":20,"tag":98,"props":4561,"children":4562},{"style":1908},[4563],{"type":30,"value":4564}," -",{"type":20,"tag":98,"props":4566,"children":4567},{"style":115},[4568],{"type":30,"value":4569},"1",{"type":20,"tag":98,"props":4571,"children":4572},{"style":105},[4573],{"type":30,"value":4574},");\n",{"type":20,"tag":98,"props":4576,"children":4578},{"class":100,"line":4577},88,[4579],{"type":20,"tag":98,"props":4580,"children":4581},{"emptyLinePlaceholder":989},[4582],{"type":30,"value":992},{"type":20,"tag":98,"props":4584,"children":4586},{"class":100,"line":4585},89,[4587,4591,4596],{"type":20,"tag":98,"props":4588,"children":4589},{"style":105},[4590],{"type":30,"value":3702},{"type":20,"tag":98,"props":4592,"children":4593},{"style":706},[4594],{"type":30,"value":4595},"respondWith",{"type":20,"tag":98,"props":4597,"children":4598},{"style":105},[4599],{"type":30,"value":3712},{"type":20,"tag":98,"props":4601,"children":4603},{"class":100,"line":4602},90,[4604,4608,4613],{"type":20,"tag":98,"props":4605,"children":4606},{"style":105},[4607],{"type":30,"value":3720},{"type":20,"tag":98,"props":4609,"children":4610},{"style":706},[4611],{"type":30,"value":4612},"match",{"type":20,"tag":98,"props":4614,"children":4615},{"style":105},[4616],{"type":30,"value":4617},"(event.request, {\n",{"type":20,"tag":98,"props":4619,"children":4621},{"class":100,"line":4620},91,[4622],{"type":20,"tag":98,"props":4623,"children":4624},{"style":105},[4625],{"type":30,"value":4626},"            ignoreSearch: hasSearch\n",{"type":20,"tag":98,"props":4628,"children":4630},{"class":100,"line":4629},92,[4631,4635,4639,4643,4647,4651,4656],{"type":20,"tag":98,"props":4632,"children":4633},{"style":105},[4634],{"type":30,"value":3785},{"type":20,"tag":98,"props":4636,"children":4637},{"style":706},[4638],{"type":30,"value":2679},{"type":20,"tag":98,"props":4640,"children":4641},{"style":105},[4642],{"type":30,"value":2278},{"type":20,"tag":98,"props":4644,"children":4645},{"style":1908},[4646],{"type":30,"value":2458},{"type":20,"tag":98,"props":4648,"children":4649},{"style":105},[4650],{"type":30,"value":2278},{"type":20,"tag":98,"props":4652,"children":4653},{"style":2160},[4654],{"type":30,"value":4655},"response",{"type":20,"tag":98,"props":4657,"children":4658},{"style":105},[4659],{"type":30,"value":2472},{"type":20,"tag":98,"props":4661,"children":4663},{"class":100,"line":4662},93,[4664,4668,4673,4678,4683],{"type":20,"tag":98,"props":4665,"children":4666},{"style":1908},[4667],{"type":30,"value":2152},{"type":20,"tag":98,"props":4669,"children":4670},{"style":105},[4671],{"type":30,"value":4672}," response ",{"type":20,"tag":98,"props":4674,"children":4675},{"style":1908},[4676],{"type":30,"value":4677},"||",{"type":20,"tag":98,"props":4679,"children":4680},{"style":706},[4681],{"type":30,"value":4682}," fetch",{"type":20,"tag":98,"props":4684,"children":4685},{"style":105},[4686],{"type":30,"value":4687},"(event.request);\n",{"type":20,"tag":98,"props":4689,"children":4691},{"class":100,"line":4690},94,[4692],{"type":20,"tag":98,"props":4693,"children":4694},{"style":105},[4695],{"type":30,"value":3847},{"type":20,"tag":98,"props":4697,"children":4699},{"class":100,"line":4698},95,[4700],{"type":20,"tag":98,"props":4701,"children":4702},{"style":105},[4703],{"type":30,"value":3855},{"type":20,"tag":98,"props":4705,"children":4707},{"class":100,"line":4706},96,[4708],{"type":20,"tag":98,"props":4709,"children":4710},{"style":105},[4711],{"type":30,"value":3863},{"type":20,"tag":21,"props":4713,"children":4714},{},[4715],{"type":30,"value":4716},"So, that's a lot to take in, but the comments above should help. Let's break down a few of the more complex bits:",{"type":20,"tag":4718,"props":4719,"children":4721},"h3",{"id":4720},"install",[4722],{"type":30,"value":4723},"Install",{"type":20,"tag":21,"props":4725,"children":4726},{},[4727],{"type":30,"value":4728},"So, you'll notice that we have add an event listener for 'install'. This is the event that gets fired when a new version of the Service Worker is installed - whether for the first time, or because the Service Worker has changed in some way. The browser does a byte-wise comparison of the downloaded script, so any change will trigger a re-install of the script.",{"type":20,"tag":21,"props":4730,"children":4731},{},[4732,4734,4740],{"type":30,"value":4733},"Often, you won't need to change anything in the script itself, but some of the cached resources have changed, and we need to tell the browser that we want to refresh the cache. In order to do this, we'll change the ",{"type":20,"tag":94,"props":4735,"children":4737},{"className":4736},[],[4738],{"type":30,"value":4739},"cacheVersion",{"type":30,"value":4741}," number, which will trigger the browser to re-install the Service Worker, triggering our install event, and allowing us to re-downloand and cache all of the updated resources.",{"type":20,"tag":4718,"props":4743,"children":4745},{"id":4744},"activate",[4746],{"type":30,"value":4747},"Activate",{"type":20,"tag":21,"props":4749,"children":4750},{},[4751,4753,4759],{"type":30,"value":4752},"After that, we have a listener on the 'activate' event. This gets triggered when our service worker takes control of the context - once it becomes 'active'. You'll notice in our 'install' event that we're calling ",{"type":20,"tag":94,"props":4754,"children":4756},{"className":4755},[],[4757],{"type":30,"value":4758},"self.skipWaiting();",{"type":30,"value":4760},". The normal behaviour is that when a new Service Worker is installed, it doesn't take over from the old Service Worker until the page is refreshed. This could be fine for your case, but in many cases, we'll want to start serving the new resources right away, so we tell the browser to skip waiting for this Service Worker, and activate it immediately, allowing it to take over from the old Service Worker (if any).",{"type":20,"tag":21,"props":4762,"children":4763},{},[4764,4766,4773],{"type":30,"value":4765},"In the activate event listener, we then clear out all of our old cache versions, leaving just the specified cache available. You'll want to take a look at the ",{"type":20,"tag":25,"props":4767,"children":4770},{"href":4768,"rel":4769},"https://developer.mozilla.org/en-US/docs/Web/API/Cache",[50],[4771],{"type":30,"value":4772},"Cache Interface",{"type":30,"value":4774}," for more details here.",{"type":20,"tag":21,"props":4776,"children":4777},{},[4778,4780,4786,4788,4794],{"type":30,"value":4779},"Finally, once we've cleared out the old caches, we post a message back to main thread of any clients we have control over that they should reload, since the cache has been updated (remember the ",{"type":20,"tag":94,"props":4781,"children":4783},{"className":4782},[],[4784],{"type":30,"value":4785},"handleMessage",{"type":30,"value":4787}," function in the install workers script?). What you want to do with this message will depend on your application, but it could be as simple as calling ",{"type":20,"tag":94,"props":4789,"children":4791},{"className":4790},[],[4792],{"type":30,"value":4793},"location.reload()",{"type":30,"value":4795}," to get the new resources from the Service Worker.",{"type":20,"tag":4718,"props":4797,"children":4799},{"id":4798},"fetch",[4800],{"type":30,"value":4801},"Fetch",{"type":20,"tag":21,"props":4803,"children":4804},{},[4805,4807,4814],{"type":30,"value":4806},"And here's where the actual offline-enabling functionality happens. You'll want to take a look at the ",{"type":20,"tag":25,"props":4808,"children":4811},{"href":4809,"rel":4810},"https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch",[50],[4812],{"type":30,"value":4813},"Fetch API",{"type":30,"value":4815}," for more details on fetch, but the point here is that we're intercepting all requests from the client (that's the browser main thread for our origin), and checking the requested resource name against our cache. If we have the resource, we return it from our cache, and otherwise we pass the request through to the network.",{"type":20,"tag":21,"props":4817,"children":4818},{},[4819],{"type":30,"value":4820},"Two things are worth noting here:",{"type":20,"tag":57,"props":4822,"children":4823},{},[4824,4835],{"type":20,"tag":61,"props":4825,"children":4826},{},[4827,4829,4833],{"type":30,"value":4828},"Our fetch intercept catches ",{"type":20,"tag":619,"props":4830,"children":4831},{},[4832],{"type":30,"value":4076},{"type":30,"value":4834}," requests - not just XMLHTTPRequests, for example, but every single request the main thread makes of the network. There's a lot that can be done with this - we're just scratching the surface here.",{"type":20,"tag":61,"props":4836,"children":4837},{},[4838,4840,4847],{"type":30,"value":4839},"The 'serve from cache if available, and otherwise pass to the network' is just one potential model for how we can handle caching and serving resources with the Service Worker - see ",{"type":20,"tag":25,"props":4841,"children":4844},{"href":4842,"rel":4843},"https://developers.google.com/web/ilt/pwa/lab-caching-files-with-service-worker",[50],[4845],{"type":30,"value":4846},"this article on Caching File with Service Worker",{"type":30,"value":4848}," for some alternative approaches.",{"type":20,"tag":35,"props":4850,"children":4851},{"id":1508},[4852],{"type":30,"value":4853},"Are We There Yet?",{"type":20,"tag":21,"props":4855,"children":4856},{},[4857,4859,4866],{"type":30,"value":4858},"Pretty much! Assuming you can mark the other items off the checklists above, your application should now be ready to serve itself as a PWA. To confirm this, you can use the ",{"type":20,"tag":25,"props":4860,"children":4863},{"href":4861,"rel":4862},"https://developers.google.com/web/tools/lighthouse/",[50],[4864],{"type":30,"value":4865},"Lighthouse Extension",{"type":30,"value":4867}," from Google to test your site, and confirm that all is as it should be.",{"type":20,"tag":21,"props":4869,"children":4870},{},[4871],{"type":30,"value":4872},"While this gives you a PWA suitable for, say, desktop use, there's still a few pieces to the puzzle before we're ready to be assigned real estate on the home screens of Android or iOS devices - we'll be going into that next time.",{"type":20,"tag":1530,"props":4874,"children":4875},{},[4876],{"type":30,"value":1534},{"title":8,"searchDepth":137,"depth":137,"links":4878},[4879,4880,4881,4882,4887],{"id":1702,"depth":111,"text":1705},{"id":1856,"depth":111,"text":1859},{"id":2385,"depth":111,"text":2388},{"id":3301,"depth":111,"text":3304,"children":4883},[4884,4885,4886],{"id":4720,"depth":137,"text":4723},{"id":4744,"depth":137,"text":4747},{"id":4798,"depth":137,"text":4801},{"id":1508,"depth":111,"text":4853},"content:ckeefer:2017-2:MorePWAToYa-Part1.md","ckeefer/2017-2/MorePWAToYa-Part1.md","ckeefer/2017-2/MorePWAToYa-Part1",{"user":1548,"name":1549},1780330271004]