[
  {
    "path": ".babelrc",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n{\n  \"env\": {\n    \"test\": {\n      \"presets\": [\"es2015\"]\n    },\n    \"default\": {\n      \"presets\": [\n        [\n          \"es2015\",\n          {\n            \"modules\": false\n          }\n        ]\n      ],\n      \"plugins\": [\"external-helpers\"]\n    }\n  }\n}\n"
  },
  {
    "path": ".eslintignore",
    "content": "/coverage\n/third_party\n/public/js/gulliver.js\n/public/js/lighthouse-chart.js\n/public/js/pwa-form.js\n/lighthouse_machine\n"
  },
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"google\",\n  // http://eslint.org/docs/rules/\n  \"rules\": {\n    \"max-len\": [2, 100, {\n      \"ignoreComments\": true,\n      \"ignoreUrls\": true,\n      \"tabWidth\": 2\n    }],\n    \"no-implicit-coercion\": [2, {\n      \"boolean\": false,\n      \"number\": true,\n      \"string\": true\n    }],\n    \"no-unused-expressions\": [2, {\n      \"allowShortCircuit\": true,\n      \"allowTernary\": false\n    }],\n    \"no-unused-vars\": [2, {\n      \"vars\": \"all\",\n      \"args\": \"after-used\",\n      \"argsIgnorePattern\": \"(^reject$|^_$)\",\n      \"varsIgnorePattern\": \"(^_$)\"\n    }],\n    \"quotes\": [2, \"single\"],\n    \"require-jsdoc\": 0,\n    \"valid-jsdoc\": 0,\n    \"prefer-arrow-callback\": 1,\n    \"no-var\": 1\n  },\n  // http://eslint.org/docs/user-guide/configuring#specifying-environments\n  \"env\": {\n    \"node\": true\n  },\n  \"parserOptions\": {\n    \"ecmaVersion\": 2017\n  }\n}\n"
  },
  {
    "path": ".gcloudignore",
    "content": "node_modules/\nlighthouse_machine/\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\nnpm-debug.log\n/coverage\n.jshintrc\n.idea/\nkey.json\npublic/js/gulliver.js\npublic/js/gulliver.js.map\npublic/firebase-messaging-sw.js\n\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: node_js\nsudo: required\ndist: trusty\nnode_js:\n  - \"8\"\nbefore_script:\n  - npm install\nscript:\n  - npm test\nenv:\n  # evade checks in config.js\n  - CLIENT_ID=placeholder CLIENT_SECRET=placeholder GCLOUD_PROJECT=placeholder CLOUD_BUCKET=placeholder FIREBASE_AUTH=placeholder API_TOKENS=\"abcdefghijk\"\n  "
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "Want to contribute? Great! First, read this page (including the small print at the end).\n\n### Before you contribute\nBefore we can use your code, you must sign the\n[Google Individual Contributor License Agreement]\n(https://cla.developers.google.com/about/google-individual)\n(CLA), which you can do online. The CLA is necessary mainly because you own the\ncopyright to your changes, even after your contribution becomes part of our\ncodebase, so we need your permission to use and distribute your code. We also\nneed to be sure of various other things—for instance that you'll tell us if you\nknow that your code infringes on other people's patents. You don't have to sign\nthe CLA until after you've submitted your code for review and a member has\napproved it, but you must do it before we can put your code into our codebase.\nBefore you start working on a larger contribution, you should get in touch with\nus first through the issue tracker with your idea so that we can help out and\npossibly guide you. Coordinating up front makes it much easier to avoid\nfrustration later on.\n\n### Code reviews\nAll submissions, including submissions by project members, require review. We\nuse Github pull requests for this purpose.\n\n### The small print\nContributions made by corporations are covered by a different agreement than\nthe one above, the\n[Software Grant and Corporate Contributor License Agreement]\n(https://cla.developers.google.com/about/google-corporate).\n"
  },
  {
    "path": "FAQ.md",
    "content": "# PWA Directory FAQ\n\n### What is PWA Directory?\nIs an open source directory of Progressive Web Apps driven by user submissions. \n\n### What are the goals of this project?\nIts goals are to help developers discover new PWAs, build a good example of a Server-Side Rendered PWA and share what we learn during the developing process.\n\n### Is this a Google product?\nNo, it was built by the Google Developer Relations team as an example for the Web developer community.\n\n### How does it rank PWAs?\nWe use [Lighthouse](https://github.com/GoogleChrome/lighthouse), that runs a set of checks validating the existence of the features, \ncapabilities, and performance that should characterize a PWA.\n\n### Why is my Lighthouse score different from the Lighthouse Chrome Extension?\nIt is important to highlight that we use a version of Lighthouse built on [Headless Chromium](https://chromium.googlesource.com/chromium/src/+/master/headless/README.md) which enables it to run as a server app, for that reason our Lighthouse score and report may deviate from the standard Lighthouse Chrome extension.\n\n### Why are you using Server Side Rendering?\nWe found that there are not that many examples of PWAs using Server Side Rendering and that many developers would benefit from one.\n\n### What technologies did you use?\n*Backend*\n - [Node.js](https://nodejs.org/en/) \n - [Express.js](http://expressjs.com/)\n - [Handlebars](http://handlebarsjs.com/)\n - [Google App Engine Node.js Flexible Environment](https://cloud.google.com/appengine/docs/flexible/nodejs/)\n\n*Frontend*\n - JavaScript (vanilla)\n - [Service Worker Precache](https://github.com/GoogleChrome/sw-precache)\n - [Service Worker Toolbox](https://github.com/GoogleChrome/sw-toolbox)\n\n*Storage*\n - [Google Cloud Datastore](https://cloud.google.com/datastore/) for general data\n - [Google Cloud Storage](https://cloud.google.com/storage/) for images only\n\n### Why are you using Javascript without a framework?\nThere is a good variety of JS frameworks out there and we love them, however we did not want to add extra overhead to developers that have not used the framework of our choice.\n\n### What do you plan for the near future?\nWe started with a basic example that we want to improve over time, our plan is to release a series of posts explaining in detail the discrete progressive enhancements from this basic Website to a high performing PWA.\n\nBeyond that, we want to track the evolution of all the PWAs submitted over time by running Lighthouse weekly, include newer metrics and features that will help developers test and build better PWAs.\n\n###Why didn’t you just collaborate with other existing PWA directories?\nWe wanted to start from scratch with a Server Side rendered solution and progressively add PWA functionalities to learn more about the process and document all the steps.\n\n### How do I request features or submit bugs?\n\nPlease submit them directly in our [GitHub issues section](https://github.com/GoogleChrome/gulliver/issues).\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015, Google Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "```diff\n! This project has been deprecated.\n```\n\n# Gulliver\n\n[Gulliver](https://pwa-directory.appspot.com/) is a directory of [Progressive Web Apps](https://infrequently.org/2016/09/what-exactly-makes-something-a-progressive-web-app/).\n\n## Contents\n\nIn Gulliver's landing page you can browse the set of currently registered PWAs as depicted in the following landing page snapshot:\n\n![Screenshot](img/gulliver-landing-page.png)\n\nIf you click on a particular PWA, Gulliver takes you to a detail page showing the results of an evaluation done on that specific PWA using the  [Lighthouse PWA Analyzer](https://www.youtube.com/watch?v=KiV2p46rWjU) tool (Details page #1), and a view of the associated [web app manifest](https://developer.mozilla.org/en-US/docs/Web/Manifest) file  for the application (Details Page #2):\n\nDetails Page #1            |  Details Page #2\n:-------------------------:|:-------------------------:\n![](img/gulliver-details-one.png)  |  ![](img/gulliver-details-two.png)\n\nGulliver itself has been implemented as a PWA; therefore it is designed to work well on any kind of device, including desktop web browsers (see landing page), and on mobile devices (see details page).\n\n## FAQ\n\n[Visit our FAQ Page](https://github.com/GoogleChrome/gulliver/blob/master/FAQ.md)\n\n## Requirements\n\nGulliver was built using the [ExpressJS](https://expressjs.com/) web framework for Node.js, and uses the [Google Cloud Platform](https://cloud.google.com/) (GCP) for computing and storage services.\n\nThe following components are required to run the project (tested on macOS):\n\n1. [NodeJS](https://nodejs.org/) (LTS version ~6.11.0). A JavaScript runtime built on Chrome's V8 JavaScript engine. (How to verify? Run `node --version`.) If you have a later version, install the LTS version with `nvm`.\n\n1. [Google Cloud SDK](https://cloud.google.com/sdk/). A set of tools for the Google Cloud Platform (GCP) that you can use to access the Google Compute Engine and the Google Cloud Storage, which are two components of GCP used by Gulliver. (How to verify? Run `gcloud --version`.)\n\n1. [Memcached](https://memcached.org/). A distributed memory object caching system. (How to verify? Run `memcached` (the command should appear to hang), and then `telnet localhost 11211` in a separate terminal. In the `telnet` window, typing `version` it should report the `memcached` version. If you don't have it, see [these instructions](https://cloud.google.com/appengine/docs/flexible/nodejs/using-redislabs-memcache#testing_memcached_locally) to install memcached.)\n\nIn addition, you will need to set up a GCP project, and configure OAuth:\n\n1. Create a [Google Cloud Platform](https://console.cloud.google.com/) project. A GCP project forms the basis of accessing the GCP. Then, run `gcloud init` to configure `gcloud` locally, if you get the error \"Could not load the default credentials\" run `gcloud auth login`.\n\n1. Get the OAuth *client id* and *client secret* associated with this project. (How to verify? There's no automatic way, but see [Creating a Google API Console project and client ID](https://developers.google.com/identity/sign-in/web/devconsole-project) for how to create one. Make sure you list `http://localhost:8080` as one of the `Authorized JavaScript origins`.)\n\nFinally (and optionally), you need a Firebase project, and the Firebase Cloud Messaging \"Server key\" and \"Sender ID\":\n\n1. Create a [Firebase](https://console.firebase.google.com/) project.\n\n1. Get Firebase Cloud Messaging \"Server key\" and \"Sender ID\" associated with this project. Select \"Project settings\" and then \"Cloud Messaging\". The URL should be of the form <https://console.firebase.google.com/project/$FIREBASE_PROJECT/settings/cloudmessaging>. (How to verify? There's no automatic way, but the \"Server key\" should be a long string of >100 characters, and the \"Sender ID\" a >10 digit number.)\n\n## Running Gulliver\n\n1. Clone the GitHub repository: `git clone https://github.com/GoogleChrome/gulliver.git`\n\n1. Switch into the project directory: `cd gulliver`\n\n1. Create indexes for the [Google Cloud Datastore](https://cloud.google.com/datastore/docs/concepts/overview): `gcloud datastore create-indexes index.yaml`\n\n1. (Optional) Deploy cron jobs for scheduled PWA updates: `gcloud app deploy cron.yaml`\n\n1. Install **Memcached** and run it on `localhost:11211`. Check these [installation instructions](https://cloud.google.com/appengine/docs/flexible/nodejs/caching-application-data) for guidance.\n\n1. Run **`npm install`** to install dependencies.\n\n1. Configure your project either via a config file or environment variables (which override the corresponding keys in the config file). To create a config file, copy the [sample config](config/config.example.json) and adjust the values accordingly:\n\n```\n$ cp config/config.example.json config/config.json\n$ vim config/config.json\n```\n\n1. Start Gulliver via `npm start`.\n\n1. Gulliver should now be running at `http://localhost:8080`.\n\n## Running Tests\n\nTo verify that everything is working properly you can run the project's tests:\n\n1. `npm test` to run lint + tests + coverage report.\n2. `npm run mocha` to run all the tests only.\n3. `npm run coverage` to run tests + coverage report.\n\n## Lighthouse PWA Analyzer\n\nGulliver reports an evaluation of the \"progressiveness\" of each registered PWA. This evaluation is done by Lighthouse, which is a tool that runs a set of checks validating the existence of the features, capabilities, and performance that should characterize a PWA. You can learn more about Lighthouse in the [GitHub repository](https://github.com/GoogleChrome/lighthouse), or in this [video](https://www.youtube.com/watch?v=KiV2p46rWjU).\n\n## References\n\nTo find out more about what PWAs are and how to go about incorporating the principles of PWAs into the development of your applications, check the following references which provide introductory information and references:\n\n+ [Progressive Web Apps](https://developers.google.com/web/#progressive-web-apps): Documentation entry point. Here you will find several resources to get started developing PWAs\n\n+ [Progressive Web Apps: Escaping Tabs without Losing our Soul](https://infrequently.org/2015/06/progressive-apps-escaping-tabs-without-losing-our-soul/):\nIntroductory article with historical perspective\n\n+ [Getting Started with Progressive Web Apps](https://addyosmani.com/blog/getting-started-with-progressive-web-apps/): Sound introduction on the fundamental elements behind the development of PWAs\n\n+ [The Building Blocks of PWAs](https://www.smashingmagazine.com/2016/09/the-building-blocks-of-progressive-web-apps/): Interesting overall view of PWAs.\n\n## License\n\nSee [LICENSE](./LICENSE) for more.\n\n## Disclaimer\n\nThis is not a Google product.\n"
  },
  {
    "path": "app.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\n// http-parser-js addresses issues such as corrupt HTTP headers\n// http://stackoverflow.com/questions/36628420/nodejs-request-hpe-invalid-header-token\nprocess.binding('http_parser').HTTPParser = require('http-parser-js').HTTPParser;\n\nconst path = require('path');\nconst express = require('express');\nconst enforce = require('express-sslify');\nconst compression = require('compression');\nconst config = require('./config/config');\nconst asset = require('./lib/asset-hashing').asset;\nconst hbs = require('hbs');\nconst helpers = require('./views/helpers');\nconst app = express();\nconst bodyParser = require('body-parser');\nconst serveStatic = require('serve-static');\nconst minifyHTML = require('express-minify-html');\nconst libPwaIndex = require('./lib/pwa-index');\n\nconst CACHE_CONTROL_SHORT_EXPIRES = 60 * 10; // 10 minutes.\nconst CACHE_CONTROL_EXPIRES = 60 * 60 * 24; // 1 day.\nconst CACHE_CONTROL_NEVER_EXPIRE = 31536000;\nconst ENVIRONMENT_PRODUCTION = 'production';\n\nif (app.get('env') === ENVIRONMENT_PRODUCTION) {\n  app.use((req, res, next) => {\n    if (req.path.startsWith('/tasks/')) {\n      next();\n    } else {\n      enforce.HTTPS({trustProtoHeader: true})(req, res, next); // eslint-disable-line new-cap\n    }\n  });\n}\n\napp.use(compression());\n\napp.disable('x-powered-by');\napp.disable('etag');\napp.set('views', path.join(__dirname, 'views'));\napp.set('view engine', 'hbs');\napp.set('trust proxy', true);\nhbs.registerPartials(path.join(__dirname, '/views/includes/'));\nhelpers.registerHelpers(hbs);\n\n// Make variables available to *all* templates\nhbs.localsAsTemplateData(app);\napp.locals.configstring = JSON.stringify({\n  /* eslint-disable camelcase */\n  client_id: config.get('CLIENT_ID'),\n  ga_id: config.get('GOOGLE_ANALYTICS'),\n  firebase_msg_sender_id: config.get('FIREBASE_MSG_SENDER_ID')\n  /* eslint-enable camelcase */\n});\n\napp.use(bodyParser.urlencoded({extended: true}));\n\nif (app.get('env') === ENVIRONMENT_PRODUCTION) {\n  app.use(minifyHTML({\n    override: true,\n    exception_url: false, // eslint-disable-line camelcase\n    htmlMinifier: {\n      removeComments: true,\n      collapseWhitespace: true,\n      collapseBooleanAttributes: true,\n      removeAttributeQuotes: true,\n      removeEmptyAttributes: true,\n      minifyJS: true\n    }\n  }));\n}\n\n// Static files\nconst staticFilesMiddleware = serveStatic(path.resolve('./public'));\napp.use((req, res, next) => {\n  const path = req.url;\n  req.url = asset.decode(path);\n  let mime = serveStatic.mime.lookup(req.url);\n  if (mime.match('image*') || req.url.includes('manifest.json')) {\n    res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_EXPIRES);\n  } else if (req.url === path) {\n    res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_SHORT_EXPIRES);\n  } else {\n    // versioned assets don't expire\n    res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_NEVER_EXPIRE);\n  }\n  staticFilesMiddleware(req, res, next);\n});\n\n// Make node_modules/{{module}} available at /{{module}}\n['sw-toolbox', 'sw-offline-google-analytics'].forEach(module => {\n  app.use(\n   '/' + module,\n   express.static('node_modules/' + module)\n );\n});\n\n// Middlewares\napp.use(require('./middlewares'));\n\n// Controllers\napp.use(require('./controllers'));\n\n// If no route has matched, return 404\napp.use((req, res) => {\n  res.status(404).render('404.hbs',\n    {nonce1: req.nonce1, nonce2: req.nonce2, contentOnly: req.query.contentOnly || false});\n});\n\n// Basic error handler\napp.use((err, req, res, _) => {\n  console.error(err);\n  if (err.status === 404) {\n    res.status(404).render('404.hbs', {nonce1: req.nonce1, nonce2: req.nonce2});\n  } else {\n    // If our routes specified a specific response, then send that. Otherwise,\n    // send a generic message so as not to leak anything.\n    res.status(500).send(err || 'Something broke!');\n  }\n});\n\nif (module === require.main) {\n  // Start the server\n  const server = app.listen(config.get('PORT'), () => {\n    const port = server.address().port;\n    console.log('App listening on port %s', port);\n  });\n\n  // Index all PWAs\n  libPwaIndex.indexAllPwas();\n}\n\nmodule.exports = app;\n"
  },
  {
    "path": "app.yaml",
    "content": "#\tCopyright 2015-2016, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\nruntime: nodejs\nenv: flexible\n\ninstance_class: B4_1G\nmanual_scaling:\n  instances: 2\n\nhandlers:\n - url: /.*\n   script: IGNORED\n   secure: always\n\nnetwork:\n  instance_tag: default-service\n"
  },
  {
    "path": "config/.gitignore",
    "content": "config.json\n"
  },
  {
    "path": "config/config.example.json",
    "content": "{\n  \"//\": \"See README.md for more information about what to put here\",\n  \"GCLOUD_PROJECT\": \"run `gcloud config get-value project`\",\n  \"CLOUD_BUCKET\": \"see https://console.cloud.google.com/storage/browser?project=$GCLOUD_PROJECT\",\n  \"CLIENT_ID\": \"see https://console.cloud.google.com/apis/credentials?project=$GCLOUD_PROJECT\",\n  \"CLIENT_SECRET\": \"see https://console.cloud.google.com/apis/credentials?project=$GCLOUD_PROJECT\",\n  \"WEBPERFORMANCE_SERVER\": \"your Web Performance server URL (optional)\",\n  \"WEBPERFORMANCE_SERVER_API_KEY\": \"your Key for the Web Performance Service\",\n  \"GOOGLE_ANALYTICS\": \"your Google Analytics tracking code (optional)\",\n  \"CANONICAL_ROOT\": \"your website root address. Can be http://localhost:8080 in development\",\n  \"FIREBASE_AUTH\": \"the 'Server key' (optional); see https://console.firebase.google.com/project/$FIREBASE_PROJECT/settings/cloudmessaging\",\n  \"FIREBASE_MSG_SENDER_ID\": \"the 'Sender ID' (optional); see https://console.firebase.google.com/project/$FIREBASE_PROJECT/settings/cloudmessaging\"\n}\n"
  },
  {
    "path": "config/config.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\n// Hierarchical node.js configuration with command-line arguments, environment\n// variables, and files.\nconst nconf = require('nconf');\nconst path = require('path');\n\nnconf\n  // 1. Command-line arguments\n  .argv()\n  // 2. Environment variables\n  .env([\n    'CLOUD_BUCKET',\n    'GCLOUD_PROJECT',\n    'PORT',\n    'CLIENT_ID',\n    'CLIENT_SECRET',\n    'WEBPERFORMANCE_SERVER',\n    'WEBPERFORMANCE_SERVER_API_KEY',\n    'GOOGLE_ANALYTICS',\n    'FIREBASE_AUTH',\n    'CANONICAL_ROOT',\n    'FIREBASE_MSG_SENDER_ID',\n    'API_TOKENS'\n  ])\n  // 3. Config file\n  .file({file: path.join(__dirname, 'config.json')})\n  // 4. Defaults\n  .defaults({\n    PORT: 8080 // Port used by HTTP server\n  });\n\n// Check for required settings\ncheckConfig('GCLOUD_PROJECT');\ncheckConfig('CLOUD_BUCKET');\ncheckConfig('CLIENT_ID');\ncheckConfig('CLIENT_SECRET');\n\nfunction checkConfig(setting) {\n  // If setting undefined, throw error\n  if (!nconf.get(setting)) {\n    throw new Error(`You must set the ${setting} environment variable or add it to ` +\n      'config/config.json!');\n  }\n  // If setting includes a space, throw error\n  if (nconf.get(setting).match(/\\s/)) {\n    throw new Error(`The ${setting} environment variable is suspicious (\"${nconf.get(setting)}\")`);\n  }\n}\n\nmodule.exports = nconf;\n"
  },
  {
    "path": "controllers/api/favorite-pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\n\nconst verifyIdToken = require('../../lib/verify-id-token');\nconst libFavoritePwa = require('../../lib/favorite-pwa');\nconst FavoritePwa = require('../../models/favorite-pwa');\nconst User = require('../../models/user');\n\n/**\n * GET /favorite-pwa/\n *\n * Returns all Favorite PWAs for a user\n */\nrouter.get('/', (req, res) => {\n  res.setHeader('Content-Type', 'application/json');\n  const idToken = req.get('Authorization');\n  if (!idToken) {\n    res.status(401);\n    res.json('401 Unauthorized');\n    return;\n  }\n\n  return verifyIdToken.verifyIdToken(idToken)\n    .then(googleLogin => {\n      const user = new User(googleLogin);\n      return libFavoritePwa.findByUserId(user.id);\n    })\n    .then(favoritePwas => {\n      if (favoritePwas) {\n        res.json(favoritePwas);\n      } else {\n        res.status(404);\n        res.json('not found');\n      }\n    })\n    .catch(err => {\n      console.error(err);\n      res.status(500);\n      res.json('Server error while loading Favorite PWAs');\n    });\n});\n\n/**\n * GET /favorite-pwa/:pwaId\n *\n * Returns a Favorite PWA for a pwaId and user\n */\nrouter.get('/:pwaId', (req, res) => {\n  res.setHeader('Content-Type', 'application/json');\n  const pwaId = req.params.pwaId;\n  const idToken = req.get('Authorization');\n  if (!idToken) {\n    res.status(401);\n    res.json('401 Unauthorized');\n    return;\n  }\n\n  return verifyIdToken.verifyIdToken(idToken)\n    .then(googleLogin => {\n      const user = new User(googleLogin);\n      return libFavoritePwa.findFavoritePwa(pwaId, user.id);\n    })\n    .then(favoritePwas => {\n      if (favoritePwas) {\n        res.json(favoritePwas);\n      } else {\n        res.status(404);\n        res.json('not found');\n      }\n    })\n    .catch(err => {\n      console.error(err);\n      res.status(500);\n      res.json('Server error while loading Favorite PWAs');\n    });\n});\n\n/**\n * POST /favorite-pwa/\n *\n * Create a Favorite PWA.\n */\nrouter.post('/', (req, res) => {\n  res.setHeader('Content-Type', 'application/json');\n  const idToken = req.body.idToken;\n  const pwaId = req.body.pwaId;\n\n  return verifyIdToken.verifyIdToken(idToken)\n    .then(googleLogin => {\n      const user = new User(googleLogin);\n      return libFavoritePwa.save(new FavoritePwa(pwaId, user.id));\n    })\n    .then(favoritePwa => {\n      if (favoritePwa) {\n        res.json(favoritePwa);\n      } else {\n        res.status(404);\n        res.json('not found');\n      }\n    })\n    .catch(err => {\n      console.error(err);\n      res.status(500);\n      res.json('Error creating Favorite PWA');\n    });\n});\n\n/**\n * DELETE /favorite-pwa/\n *\n * Delete a Favorite PWA.\n */\nrouter.delete('/', (req, res) => {\n  res.setHeader('Content-Type', 'application/json');\n  const idToken = req.body.idToken;\n  const pwaId = req.body.pwaId;\n\n  return verifyIdToken.verifyIdToken(idToken)\n    .then(googleLogin => {\n      const user = new User(googleLogin);\n      return libFavoritePwa.findFavoritePwa(pwaId, user.id);\n    })\n    .then(favoritePwa => {\n      if (favoritePwa) {\n        libFavoritePwa.delete(favoritePwa.id)\n          .then(_ => {\n            res.status(200);\n            res.json(favoritePwa);\n          })\n          .catch(err => {\n            console.error(err);\n            res.status(500);\n            res.json('Error deleting favorite PWA');\n          });\n      } else {\n        res.status(404);\n        res.json('not found');\n      }\n    })\n    .catch(err => {\n      console.error(err);\n      res.status(500);\n      res.json('Error deleting favorite PWA');\n    });\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/api/index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\n\n// Includes APIs for Lighthouse (/api/lighthouse)\nrouter.use('/lighthouse', require('./lighthouse'));\n\n// Includes APIs for Notifications (/api/notifications)\nrouter.use('/notifications', require('./notifications'));\n\n// Includes APIs for FavoritePwas (/api/favoritepwa)\nrouter.use('/favorite-pwa', require('./favorite-pwa'));\n\n// Includes APIs for PWAs (/api/pwa)\nrouter.use('/pwa', require('./pwa'));\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/api/lighthouse.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst lighthouseLib = require('../../lib/lighthouse');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst CACHE_CONTROL_EXPIRES = 60 * 60 * 24; // 1 day.\n\n/**\n * GET /api/lighthouse-graph/:pwaId\n *\n * Returns the Lighthouse Graph information for a PWA\n * it uses the Google Charts JSON format:\n *  https://developers.google.com/chart/interactive/docs/reference#dataparam\n */\nrouter.get('/graph/:pwaId', (req, res) => {\n  res.setHeader('Content-Type', 'application/json');\n\n  lighthouseLib.getLighthouseGraphByPwaId(req.params.pwaId)\n    .then(lighthouseGraph => {\n      if (lighthouseGraph) {\n        res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_EXPIRES);\n        res.json(lighthouseGraph);\n      } else {\n        res.status(404);\n        res.json('not found');\n      }\n    })\n    .catch(err => {\n      res.status(500);\n      res.json(err);\n    });\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/api/notifications.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst bodyParser = require('body-parser');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst notificationsLib = require('../../lib/notifications');\nconst jsonParser = bodyParser.json();\n\nrouter.get('/topics/', (req, res) => {\n  const token = req.query.token;\n  notificationsLib.list(token)\n    .then(subscriptions => {\n      res.json({\n        subscriptions: subscriptions\n      });\n    })\n    .catch(err => {\n      res.status(500);\n      res.json(err);\n    });\n});\n\nrouter.post('/subscribe/:topic/', jsonParser, (req, res) => {\n  const token = req.body.token;\n  const topic = req.params.topic;\n  notificationsLib.subscribe(token, topic)\n    .then(_ => {\n      res.json({success: true});\n    })\n    .catch(err => {\n      res.status(500);\n      res.json(err);\n    });\n});\n\nrouter.post('/unsubscribe/:topic/', jsonParser, (req, res) => {\n  const token = req.body.token.trim();\n  const topic = req.params.topic;\n  notificationsLib.unsubscribe(token, topic)\n    .then(_ => {\n      res.json({success: true});\n    })\n    .catch(err => {\n      res.status(500);\n      res.json(err);\n    });\n});\n\nmodule.exports = router;\n\n"
  },
  {
    "path": "controllers/api/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nrequire('express-csv');\nconst pwaLib = require('../../lib/pwa');\nconst libMetadata = require('../../lib/metadata');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst verifyIdToken = require('../../lib/verify-id-token');\nconst bodyParser = require('body-parser');\nconst Pwa = require('../../models/pwa');\nconst color = require('../../lib/color');\nconst CACHE_CONTROL_EXPIRES = 60 * 60 * 1; // 1 hour\nconst RSS = require('rss');\nconst {URL} = require('url');\n\nfunction getDate(date) {\n  return new Date(date).toISOString().split('T')[0];\n}\n\nclass CsvWriter {\n  write(result, pwas) {\n    const csv = [];\n    pwas.forEach(pwa => {\n      const created = getDate(pwa.created);\n      const updated = getDate(pwa.updated);\n      const csvLine = [];\n      csvLine.push(pwa.id);\n      csvLine.push(pwa.absoluteStartUrl);\n      csvLine.push(pwa.manifestUrl);\n      csvLine.push(pwa.lighthouseScore);\n      csvLine.push(created);\n      csvLine.push(updated);\n      csv.push(csvLine);\n    });\n    result.setHeader('Content-Type', 'text/csv');\n    csv.unshift(\n      ['id', 'absoluteStartUrl', 'manifestUrl', 'lighthouseScore', 'created', 'updated']);\n    result.csv(csv);\n  }\n}\n\nclass JsonWriter {\n  write(result, pwas) {\n    const pwaList = [];\n    pwas.forEach(dbPwa => {\n      const created = getDate(dbPwa.created);\n      const updated = getDate(dbPwa.updated);\n      const pwa = {};\n      pwa.id = dbPwa.id;\n      pwa.absoluteStartUrl = dbPwa.absoluteStartUrl;\n      pwa.manifestUrl = dbPwa.manifestUrl;\n      pwa.lighthouseScore = dbPwa.lighthouseScore;\n      pwa.webPageTest = dbPwa.webPageTest;\n      pwa.pageSpeed = dbPwa.pageSpeed;\n      pwa.created = created;\n      pwa.updated = updated;\n      pwaList.push(pwa);\n    });\n    result.setHeader('Content-Type', 'application/json');\n    result.json(pwaList);\n  }\n}\n\nfunction render(res, view, options) {\n  return new Promise((resolve, reject) => {\n    res.render(view, options, (err, html) => {\n      if (err) {\n        console.log(err);\n        reject(err);\n      }\n      resolve(html);\n    });\n  });\n}\n\nfunction renderOnePwaRss(pwa, req, res) {\n  const url = req.originalUrl;\n  const contentOnly = false || req.query.contentOnly;\n  let arg = Object.assign(libMetadata.fromRequest(req, url), {\n    pwa: pwa,\n    title: 'PWA Directory: ' + pwa.name,\n    description: 'PWA Directory: ' + pwa.name + ' - ' + pwa.description,\n    backlink: true,\n    contentOnly: contentOnly\n  });\n  return render(res, 'pwas/view-rss.hbs', arg);\n}\n\nasync function asyncForEach(array, callback) {\n  for (let index = 0; index < array.length; index++) {\n    await callback(array[index], index, array);\n  }\n}\n\nclass RssWriter {\n  write(req, res, pwas) {\n    const feed = new RSS({\n      /* eslint-disable camelcase */\n      title: 'PWA Directory',\n      description: 'A Directory of Progressive Web Apps',\n      feed_url: 'https://pwa-directory.appspot.com/api/pwa/?format=rss',\n      site_url: 'https://pwa-directory.appspot.com/',\n      image_url: 'https://pwa-directory.appspot.com/favicons/android-chrome-144x144.png',\n      pubDate: new Date(),\n      custom_namespaces: {\n        rdf: 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',\n        l: 'http://purl.org/rss/1.0/modules/link/',\n        media: 'http://search.yahoo.com/mrss/',\n        content: 'http://purl.org/rss/1.0/modules/content/'\n      }\n    });\n\n    const start = async _ => {\n      await asyncForEach(pwas, async pwa => {\n        let html = await renderOnePwaRss(pwa, req, res);\n\n        const customElements = [];\n        customElements.push({'content:encoded': html});\n        customElements.push({'l:link': {_attr: {'l:rel': 'http://purl.org/rss/1.0/modules/link/#alternate',\n          'l:type': 'application/json',\n          'rdf:resource': 'https://pwa-directory.appspot.com/api/pwa/' + pwa.id}}});\n        if (pwa.iconUrl128) {\n          customElements.push({'media:thumbnail': {_attr: {url: pwa.iconUrl128,\n            height: '128', width: '128'}}});\n        }\n\n        feed.item({\n          title: pwa.displayName,\n          url: 'https://pwa-directory.appspot.com/pwas/' + pwa.id,\n          description: html,\n          guid: pwa.id,\n          date: pwa.created,\n          custom_elements: customElements\n        });\n      });\n      res.setHeader('Content-Type', 'application/rss+xml');\n      res.status(200).send(feed.xml());\n    };\n    start();\n    /* eslint-enable camelcase */\n  }\n}\n\nconst csvWriter = new CsvWriter();\nconst jsonWriter = new JsonWriter();\nconst rssWriter = new RssWriter();\n\n/**\n * GET /api/pwa\n *\n  * Returns all PWAs as JSON, ?format=csv for CSV or ?format=rss for RSS feed\n */\nrouter.get('/:id*?', (req, res) => {\n  let format = req.query.format || 'json';\n  let sort = req.query.sort || 'newest';\n  let skip = parseInt(req.query.skip, 10);\n  let limit = parseInt(req.query.limit, 10) || 100;\n  res.setHeader('Cache-Control', 'public, max-age=' + CACHE_CONTROL_EXPIRES);\n\n  let queryPromise = req.params.id ? pwaLib.find(req.params.id) : pwaLib.list(skip, limit, sort);\n  queryPromise\n  .then(result => {\n    result = result.pwas ? result : {pwas: [result]};\n    switch (format) {\n      case 'csv': {\n        csvWriter.write(res, result.pwas);\n        break;\n      }\n      case 'rss': {\n        rssWriter.write(req, res, result.pwas);\n        break;\n      }\n      default: {\n        jsonWriter.write(res, result.pwas);\n      }\n    }\n  })\n  .catch(err => {\n    console.log(err);\n    let code = err.code || 500;\n    res.status(code);\n    res.json(err);\n  });\n});\n\nrouter.post('/add', bodyParser.json(), (req, res) => {\n  const idToken = req.body.idToken;\n\n  if (!idToken) {\n    res.status(400).send({error: 'user not logged in'});\n    return;\n  }\n\n  const manifestUrl = req.body.manifestUrl;\n  if (!manifestUrl) {\n    res.status(400).send({error: 'no manifest provided'});\n    return;\n  }\n\n  try {\n    const url = new URL(manifestUrl);\n    (async () => {\n      try {\n        const pwa = new Pwa(url.toString());\n        const user = await verifyIdToken.verifyIdToken(idToken);\n        pwa.setUser(user);\n        const savedPwa = await pwaLib.createOrUpdatePwa(pwa);\n        res.json({\n          id: savedPwa.id,\n          name: savedPwa.name,\n          backgroundColor: savedPwa.backgroundColor,\n          foregroundColor: color.bestContrastRatio('#FFFFFF', '#000000', savedPwa.backgroundColor)\n        });\n      } catch (e) {\n        const message = e.message || e;\n        res.status(400).json({error: message});\n      }\n    })();\n  } catch (e) {\n    res.status(400).send({error: 'manifestUrl is not an URL'});\n  }\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/app.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\n\nrouter.get('/shell', (req, res) => {\n  res.render('app/shell.hbs');\n});\n\nrouter.get('/offline', (req, res, next) => { // eslint-disable-line no-unused-vars\n  res.render('app/offline.hbs');\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/cache.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst libCache = require('../lib/data-cache');\n\nconst CACHE_LIFETIME = 60 * 60; // 1 hour\n\n/**\n * GET *\n *\n * Serves cached HTML or\n * overrides res.send to be able to cache rendered HTML before sending.\n */\nrouter.get('*', (req, res, next) => {\n  const url = req.originalUrl;\n  libCache.get(url)\n    .then(cachedHtml => {\n      console.log('From cache: ' + url);\n      res.send(cachedHtml);\n    })\n    .catch(_ => {\n      // Overrides res.send to be able to cache before sending.\n      res.sendResponse = res.send;\n      res.send = body => {\n        libCache.set(url, body, CACHE_LIFETIME)\n          .then(_ => {\n            libCache.storeCachedUrls(url);\n            console.log('Stored in cache: ' + url);\n          })\n          .catch(_ => {\n            console.log('Error setting cache for: ' + url);\n          });\n        res.sendResponse(body);\n      };\n      next();\n    });\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst config = require('../config/config');\nconst bodyParser = require('body-parser');\n\nrouter.use(bodyParser.json());\n\n// API\nrouter.use('/api', require('./api'));\n\n// Tasks\nrouter.use('/tasks', require('./tasks'));\n\n// PWAs\nrouter.use('/pwas', require('./pwa'));\n\nrouter.get('/', (req, res) => {\n  req.url = '/pwas';\n  router.handle(req, res);\n});\n\nrouter.get('/installable', (req, res) => {\n  req.url = '/pwas/installable';\n  router.handle(req, res);\n});\n\n// ServiceWorker\nrouter.use('/js', require('./sw'));\n\n// /.shell hosts app shell dependencies\nrouter.use('/.app', require('./app'));\n\n/**\n * This route is used to send config.json to firebase-messaging-sw.js\n */\nrouter.get('/messaging-config.json', (req, res) => {\n  // eslint-disable-next-line camelcase\n  res.json({firebase_msg_sender_id: config.get('FIREBASE_MSG_SENDER_ID')});\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst dataFetcher = require('../lib/data-fetcher');\nconst pwaLib = require('../lib/pwa');\nconst libPwaIndex = require('../lib/pwa-index');\nconst verifyIdToken = require('../lib/verify-id-token');\nconst lighthouseLib = require('../lib/lighthouse');\nconst Pwa = require('../models/pwa');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst libMetadata = require('../lib/metadata');\n\nconst LIST_PAGE_SIZE = 32;\nconst DEFAULT_PAGE_NUMBER = 1;\nconst DEFAULT_SORT_ORDER = 'newest';\nconst DEFAULT_TAB = 'installable';\nconst DEFAULT_FILTER = {\n  installable: true\n};\n\n/**\n * Setup the list template view state\n */\nfunction setupListViewState(req) {\n  const viewState = {};\n  if (typeof req.query.query === 'undefined') {\n    viewState.mainPage = true;\n    viewState.backlink = false;\n    viewState.search = false;\n  } else {\n    viewState.mainPage = true;\n    viewState.backlink = true;\n    viewState.search = true;\n    viewState.searchQuery = req.query.query;\n  }\n  viewState.contentOnly = false || req.query.contentOnly;\n  viewState.pageNumber = parseInt(req.query.page, 10) || DEFAULT_PAGE_NUMBER;\n  viewState.sortOrder = req.query.sort || DEFAULT_SORT_ORDER;\n  viewState.start = parseInt(req.query.start, 10) || (viewState.pageNumber - 1) * LIST_PAGE_SIZE;\n  viewState.limit = parseInt(req.query.limit, 10) || LIST_PAGE_SIZE;\n  viewState.end = viewState.pageNumber * LIST_PAGE_SIZE;\n  return viewState;\n}\n\n/**\n * Setup the list template view arguments\n */\nfunction setupListViewArguments(req, viewState, result) {\n  return Object.assign(libMetadata.fromRequest(req), {\n    title: 'PWA Directory',\n    description: 'PWA Directory: A Directory of Progressive Web Apps',\n    pwas: result.pwas,\n    hasNextPage: result.hasMore,\n    hasPreviousPage: viewState.pageNumber > 1,\n    nextPageNumber: viewState.pageNumber + 1,\n    previousPageNumber: (viewState.pageNumber === 2) ? false : viewState.pageNumber - 1,\n    currentPageNumber: viewState.pageNumber,\n    sortOrder: (viewState.sortOrder === DEFAULT_SORT_ORDER) ? false : viewState.sortOrder,\n    currentTab: req.path.substring(1, req.path.length) || DEFAULT_TAB,\n    startPwa: viewState.start + 1,\n    mainPage: viewState.mainPage,\n    search: viewState.search,\n    backlink: viewState.backlink,\n    searchQuery: viewState.searchQuery,\n    contentOnly: viewState.contentOnly\n  });\n}\n\nfunction listPwas(req, res, next, sortOrder, filters) {\n  const viewState = setupListViewState(req);\n  pwaLib.list(viewState.start, viewState.limit, sortOrder, filters)\n    .then(result =>\n      render(res, 'pwas/list.hbs', setupListViewArguments(req, viewState, result)))\n    .then(html => res.send(html))\n    .catch(err => {\n      err.status = 500;\n      next(err);\n    });\n}\n\n/**\n * GET /\n *\n * Display a page of PWAs (up to LIST_PAGE_SIZE at a time)\n */\nrouter.get('/', (req, res, next) => {\n  listPwas(req, res, next, DEFAULT_SORT_ORDER, DEFAULT_FILTER);\n});\n\n/**\n * GET /newest\n *\n * Display a page of PWAs sorted by score.\n */\nrouter.get('/newest', (req, res, next) => {\n  listPwas(req, res, next, 'newest');\n});\n\n/**\n * GET /score\n *\n * Display a page of PWAs sorted by score.\n */\nrouter.get('/score', (req, res, next) => {\n  listPwas(req, res, next, 'score');\n});\n\n/**\n * GET /installable\n *\n * Display a page of installable PWAs.\n */\nrouter.get('/installable', (req, res, next) => {\n  const filters = {\n    installable: true\n  };\n  listPwas(req, res, next, 'newest', filters);\n});\n\n/**\n * GET /pwas/search\n *\n * Display a search result page of PWAs\n */\nrouter.get('/search', (req, res, next) => {\n  const viewState = setupListViewState(req);\n  libPwaIndex.searchPwas(viewState.searchQuery).then(result =>\n    render(res, 'pwas/list.hbs', setupListViewArguments(req, viewState, result)))\n  .then(html => res.send(html))\n  .catch(err => {\n    err.status = 500;\n    next(err);\n  });\n});\n\n/**\n * GET /pwas/add\n *\n * Display a form for creating a PWA.\n */\nrouter.get('/add', async (req, res, next) => {\n  try {\n    const contentOnly = req.query.contentOnly || false;\n\n    const url = req.query.url || '';\n    let manifestUrl = req.query.manifestUrl || '';\n    if (url !== '' && manifestUrl !== '') {\n      const err = new Error('only one of url or manifestUrl may be set');\n      err.status = 400;\n      throw err;\n    }\n    if (url !== '') {\n      manifestUrl = await dataFetcher.fetchLinkRelManifestUrl(url);\n    }\n\n    let arg = Object.assign(libMetadata.fromRequest(req), {\n      title: 'PWA Directory - Submit a PWA',\n      description: 'PWA Directory: Submit a Progressive Web Apps',\n      pwa: {},\n      action: 'Add',\n      backlink: true,\n      submit: true,\n      contentOnly,\n      manifestUrl\n    });\n    res.render('pwas/form.hbs', arg);\n  } catch (err) {\n    next(err);\n  }\n});\n\n/**\n * POST /pwas/add\n *\n * Create a PWA.\n */\nrouter.post('/add', (req, res, next) => {\n  let manifestUrl = req.body.manifestUrl.trim();\n  if (manifestUrl.startsWith('http://')) {\n    manifestUrl = manifestUrl.replace('http://', 'https://');\n  }\n  const idToken = req.body.idToken;\n  let pwa = new Pwa(manifestUrl);\n\n  if (!manifestUrl || !idToken) {\n    let arg = Object.assign(libMetadata.fromRequest(req), {\n      pwa,\n      backlink: true,\n      error: (manifestUrl) ? 'user not logged in' : 'no manifest provided'\n    });\n    res.render('pwas/form.hbs', arg);\n    return;\n  }\n\n  verifyIdToken.verifyIdToken(idToken)\n    .then(user => {\n      pwa.setUser(user);\n      return pwaLib.createOrUpdatePwa(pwa);\n    })\n    .then(savedData => {\n      res.redirect(req.baseUrl + '/' + savedData.id);\n      return;\n    })\n    .catch(err => {\n      if (typeof err === 'number') {\n        switch (err) {\n          case pwaLib.E_MANIFEST_INVALID_URL:\n            err = `pwa.manifestUrl [${pwa.manifestUrl}] is not a valid URL`;\n            break;\n          case pwaLib.E_MISING_USER_INFORMATION:\n            err = 'Missing user information';\n            break;\n          case pwaLib.E_MANIFEST_URL_MISSING:\n            err = 'Missing manifestUrl';\n            break;\n          case pwaLib.E_NOT_A_PWA:\n            err = 'pwa is not an instance of Pwa';\n            break;\n          default:\n            return next(err);\n        }\n      }\n      // Transform err from an array of strings (in a particular format) to a\n      // comma-separated string.\n      if (Array.isArray(err)) {\n        const s = err.map(e => {\n          const m = e.match(/^ERROR:\\s+(.*)\\.$/);\n          return m ? m[1] : e; // if no match (format changed?), just return the string\n        }).join(', ');\n        err = s;\n      }\n      let arg = Object.assign(libMetadata.fromRequest(req), {\n        pwa,\n        backlink: true,\n        error: err\n      });\n      res.render('pwas/form.hbs', arg);\n      return;\n    });\n});\n\n/**\n * GET /pwas/:id\n *\n * Display a PWA or redirects to the encodedStartUrl of the PWA.\n */\nrouter.get('/:pwa', (req, res, next) => {\n  renderOnePwa(req, res)\n    .then(html => {\n      res.send(html);\n    })\n    .catch(err => {\n      err.status = 404;\n      return next(err);\n    });\n  return;\n});\n\n/**\n * Generate the HTML with 'pwas/view.hbs' for one PWA\n */\nfunction renderOnePwa(req, res) {\n  const url = req.originalUrl;\n  const pwaId = encodeURIComponent(req.params.pwa);  // we have foo/ here, need foo%2F\n  const contentOnly = false || req.query.contentOnly;\n  return pwaLib.find(pwaId)\n    .then(pwa => {\n      return lighthouseLib.findByPwaId(pwaId)\n        .then(lighthouse => {\n          if (lighthouse && lighthouse.lighthouseInfo &&\n              Object.prototype.toString.call(lighthouse.lighthouseInfo) === '[object String]') {\n            lighthouse.lighthouseInfo = JSON.parse(lighthouse.lighthouseInfo);\n          }\n          let arg = Object.assign(libMetadata.fromRequest(req, url), {\n            pwa: pwa,\n            lighthouse: lighthouse,\n            rawManifestJson: JSON.parse(pwa.manifest.raw),\n            title: 'PWA Directory: ' + pwa.name,\n            description: 'PWA Directory: ' + pwa.name + ' - ' + pwa.description,\n            backlink: true,\n            contentOnly: contentOnly\n          });\n          return render(res, 'pwas/view.hbs', arg);\n        });\n    });\n}\n\n/**\n * res.render as a Promise\n */\nfunction render(res, view, options) {\n  return new Promise((resolve, reject) => {\n    res.render(view, options, (err, html) => {\n      if (err) {\n        console.log(err);\n        reject(err);\n      }\n      resolve(html);\n    });\n  });\n}\n\n/**\n * Errors on \"/pwas/*\" routes.\n */\nrouter.use((err, req, res, next) => {\n  // Format error and forward to generic error handler for logging and\n  // responding to the request\n  err.response = err.message;\n  console.error(err);\n  next(err);\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/sw.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst router = express.Router(); // eslint-disable-line new-cap\nconst asset = require('../lib/asset-hashing').asset;\n\nconst ASSETS = JSON.stringify([\n  '/css/style.css',\n  '/js/gulliver.js'\n].map(assetPath => asset.encode(assetPath)));\n\nconst ASSETS_JS = `const ASSETS = ${ASSETS};`;\n\nrouter.get('/sw-assets-precache.js', (req, res) => {\n  res.setHeader('Content-Type', 'application/javascript');\n  res.setHeader('Cache-Control', 'no-cache, max-age=0');\n  res.send(ASSETS_JS);\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "controllers/tasks.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst pwaLib = require('../lib/pwa');\nconst tasksLib = require('../lib/tasks');\nconst Task = require('../models/task');\nconst router = express.Router(); // eslint-disable-line new-cap\n\nconst APP_ENGINE_CRON = 'X-Appengine-Cron';\n\n/**\n * Checks for the presence of the 'X-Appengine-Cron' header on the request.\n * Only requests from the App Engine cron are allowed.\n */\nfunction checkAppEngineCron(req, res, next) {\n  if (!req.get(APP_ENGINE_CRON)) {\n    return res.sendStatus(403);\n  }\n  return next();\n}\n\n/**\n * Creates a pwaLib.createOrUpdatePwa task for the given pwaId\n */\nfunction createOrUpdatePwaTasks(pwaList) {\n  const modulePath = require.resolve('../lib/pwa');\n  pwaList.forEach(pwa => {\n    tasksLib.push(new Task(pwa.id, modulePath, 'createOrUpdatePwa', 0));\n  });\n}\n\n/**\n * GET /tasks/cron\n *\n * We use a GET from the cron job to launch a PWA update process\n * for all PWAs.\n *\n * Uses checkAppEngineCron to allow only request from cron job.\n */\nrouter.get('/cron', checkAppEngineCron, (req, res, next) => {\n  pwaLib.list(undefined, undefined, 'newest')\n    .then(result => {\n      // Create one update task for each PWA\n      createOrUpdatePwaTasks(result.pwas);\n      res.sendStatus(200);\n    })\n    .catch(err => {\n      next(err);\n    });\n});\n\n/**\n * GET /tasks/updateunscored\n *\n * We use a GET from the cron job to launch a PWA update process\n * for all PWAs.\n *\n * Uses checkAppEngineCron to allow only request from cron job.\n */\nrouter.get('/updateunscored', checkAppEngineCron, (req, res, next) => {\n  return pwaLib.list(undefined, undefined, 'newest')\n    .then(result => {\n      // Create one update task for each unscored PWA\n      createOrUpdatePwaTasks(result.pwas.filter(pwa => !pwa.lighthouseScore));\n      res.sendStatus(200);\n    })\n    .catch(err => {\n      next(err);\n    });\n});\n\n/**\n * GET /tasks/execute?tasks=1\n *\n * We use a GET from the cron job to execute each PWA update task\n * The tasks parameter is the number of tasks to execute per run\n *\n * Uses checkAppEngineCron to allow only request from cron job.\n */\nrouter.get('/execute', checkAppEngineCron, (req, res) => {\n  const tasksToExecute = req.query.tasks ? req.query.tasks : 1;\n  // const tasksList = [];\n\n  (async () => {\n    const tasks = await tasksLib.getTasks(tasksToExecute);\n    console.log(`Executing ${tasks.length} tasks`);\n\n    for (let task of tasks) {\n      try {\n        console.log(`Will Execute Task: ${task.id}`);\n        // Delete before executing, so we ensure that if the task breaks\n        // something it is removed from the queue anyway.\n        try {\n          await tasksLib.deleteTask(task.id);\n        } catch (err) {\n          console.error(`Error deleting task: ${task.id}`);\n        }\n        await tasksLib.executePwaTask(task);\n        console.log(`Executed Task: ${task.id}`);\n      } catch (err) {\n        console.error(`Failed to execute task: ${task.id}`);\n      }\n    }\n    res.sendStatus(200);\n  })();\n});\n\n/**\n * Errors on \"/task/*\" routes.\n */\nrouter.use((err, req, res, next) => {\n  // Format error and forward to generic error handler for logging and\n  // responding to the request\n  err.response = {\n    message: err.message,\n    internalCode: err.code\n  };\n  next(err);\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "cron.yaml",
    "content": "cron:\n- description: (Node) Daily PWA info update job\n  url: /tasks/cron\n  schedule: every day 13:00\n\n- description: (Node) Execute PWA update tasks\n  url: /tasks/execute?tasks=30\n  schedule: every 1 minutes\n\n- description: (Node) Update unscored PWAs\n  url: /tasks/updateunscored\n  schedule: every 1 hours\n\n- description: Update unscored PWAs\n  url: /taskcreator/task?unscored=true\n  schedule: every 1 hours\n  target: web-performance\n\n- description: UpdateManifestTask\n  url: /taskcreator/task/UpdateManifestTask\n  schedule: every day 16:00\n  target: web-performance\n\n- description: UpdateIconTask\n  url: /taskcreator/task/UpdateIconTask\n  schedule: every monday 01:00\n  target: web-performance\n\n- description: PageSpeedReportTask\n  url: /taskcreator/task/PageSpeedReportTask\n  schedule: every friday 01:00\n  target: web-performance\n\n- description: WebPageTestReportTask\n  url: /taskcreator/task/WebPageTestReportTask\n  schedule: every sunday 01:00\n  target: web-performance\n"
  },
  {
    "path": "firebase-messaging-sw-generator.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst fs = require('fs');\nconst template = require('lodash.template');\nconst config = require('./config/config');\n\nconst firebaseMsgSenderId = config.get('FIREBASE_MSG_SENDER_ID');\n\nfs.readFile('./firebase-messaging-sw.tmpl', 'utf8', (error, data) => {\n  if (error) {\n    console.error('Error reading template: ', error);\n    return;\n  }\n\n  const firebaseMessagingSwFileContent = template(data)({\n    firebaseMsgSenderId: firebaseMsgSenderId\n  });\n\n  fs.writeFile('./public/firebase-messaging-sw.js', firebaseMessagingSwFileContent, err => {\n    if (err) {\n      console.log('Error Writing firebase-messaging-sw: ', err);\n    }\n  });\n});\n"
  },
  {
    "path": "firebase-messaging-sw.tmpl",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env serviceworker, browser */\n/* global firebase */\nimportScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-app.js');\nimportScripts('https://www.gstatic.com/firebasejs/3.5.2/firebase-messaging.js');\n\nfirebase.initializeApp({\n  messagingSenderId: '<%= firebaseMsgSenderId %>'\n});\nconst messaging = firebase.messaging();\nmessaging.setBackgroundMessageHandler(_ => {\n  return self.registration.showNotification();\n});\n"
  },
  {
    "path": "index.yaml",
    "content": "indexes:\n- kind: Lighthouse\n  properties:\n  - name: pwaId\n    direction: asc\n  - name: date\n    direction: desc\n- kind: PWA\n  properties:\n  - name: installable\n  - name: lighthouseScore\n    direction: desc\n- kind: PWA\n  properties:\n  - name: installable\n  - name: created\n    direction: desc\n"
  },
  {
    "path": "lib/asset-hashing.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst fs = require('fs');\nconst path = require('path');\nconst revHash = require('rev-hash');\n\nconst CHECKSUM_LENGTH = 10;\nconst CHECKSUM_PATTERN = /^[0-9a-z]{10}$/;\n\nclass ChecksumProvider {\n\n  constructor(root) {\n    this.root_ = root;\n  }\n\n  get(assetPath) {\n    const buffer = fs.readFileSync(path.join(this.root_, assetPath));\n    return revHash(buffer);\n  }\n\n}\n\nclass AssetChecksum {\n\n  constructor(checksumProvider) {\n    this.checksumProvider_ = checksumProvider;\n    this.checksumCache_ = {};\n  }\n\n  encode(assetPath) {\n    if (!assetPath) {\n      return assetPath;\n    }\n    let result = this.checksumCache_[assetPath];\n    if (result) {\n      return result;\n    }\n    const checksum = this.checksumProvider_.get(assetPath);\n    const index = assetPath.lastIndexOf('.');\n    if (index === -1) {\n      return assetPath;\n    }\n    result = assetPath.substring(0, index) +\n      '.' +\n      checksum +\n      assetPath.substring(index, assetPath.length);\n    this.checksumCache_[assetPath] = result;\n    return result;\n  }\n\n  decode(assetPath) {\n    if (!assetPath) {\n      return assetPath;\n    }\n    const fragments = assetPath.split('.');\n    if (fragments.length <= 1) {\n      return assetPath;\n    }\n    const checksumIndex = fragments.length - 2;\n    if (!fragments[checksumIndex].match(CHECKSUM_PATTERN)) {\n      return assetPath;\n    }\n    fragments.splice(checksumIndex, 1);\n    return fragments.join('.');\n  }\n\n}\n\nmodule.exports.asset = new AssetChecksum(new ChecksumProvider('public'));\n\n// Exported for testing\nmodule.exports.ChecksumProvider = ChecksumProvider;\nmodule.exports.AssetChecksum = AssetChecksum;\nmodule.exports.CHECKSUM_LENGTH = CHECKSUM_LENGTH;\n"
  },
  {
    "path": "lib/color.js",
    "content": "/**\n * Copyright 2015-2018, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst parseColor = require('parse-color');\n\nfunction bestContrastRatio(color1, color2, background) {\n  return contrastRatio(color1, background) > contrastRatio(color2, background) ? color1 : color2;\n}\n\n/**\n * Calculates the contrast ratio, as described on https://www.w3.org/TR/WCAG20/#contrast-ratiodef\n *\n * @param {string} foreground the foreground color.\n * @param {string} background the background color, Defaults to #FFFFFF.\n * @returns {Number} the contrast ration.\n */\nfunction contrastRatio(foreground, background = '#FFFFFF') {\n  if (background.trim() === 'transparent') background = 'white';\n\n  const bgLuminance = relativeLuminance(background);\n  const fgLuminance = relativeLuminance(foreground);\n\n  let darker;\n  let lighter;\n  if (fgLuminance > bgLuminance) {\n    lighter = fgLuminance;\n    darker = bgLuminance;\n  } else {\n    lighter = bgLuminance;\n    darker = fgLuminance;\n  }\n\n  return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Calculates the relative luminance, as described on https://www.w3.org/TR/WCAG20/#relativeluminancedef\n *\n * @param {string} color the foreground color.\n * @returns {Number} the relative luminance.\n */\nfunction relativeLuminance(color) {\n  let colorRed;\n  let colorGreen;\n  let colorBlue;\n\n  try {\n    [colorRed, colorGreen, colorBlue] = parseColor(color.trim()).rgb;\n  } catch (error) {\n    throw new Error('Error parsing Color with parseColor' + error);\n  }\n  let red = componentRelativeLuminance_(colorRed);\n  let green = componentRelativeLuminance_(colorGreen);\n  let blue = componentRelativeLuminance_(colorBlue);\n\n  return 0.2126 * red + 0.7152 * green + 0.0722 * blue;\n}\n\n/**\n * Generates the luminance of a single color component.\n * @param {Number} component the value to have the luminance calculated\n * @returns {Number} the calculated luminance of the color component.\n */\nfunction componentRelativeLuminance_(component) {\n  let c = component / 255;\n  return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);\n}\n\nmodule.exports = {\n  contrastRatio: contrastRatio,\n  relativeLuminance: relativeLuminance,\n  bestContrastRatio: bestContrastRatio\n};\n"
  },
  {
    "path": "lib/data-cache.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\nconst Memcached = require('memcached');\nconst memcachedAddr = process.env.MEMCACHE_PORT_11211_TCP_ADDR ||\n  config.get('MEMCACHED_SERVER') || 'localhost';\nconst memcachedPort = process.env.MEMCACHE_PORT_11211_TCP_PORT || '11211';\nconst memcached = new Memcached(memcachedAddr + ':' + memcachedPort, {timeout: 600, retries: 1});\n\nconst PAGELIST_URLS = 'PAGELIST_URLS';\nconst CACHE_LIFETIME = 60 * 60 * 6; // 6 hours\n\n/**\n * Gets a value from memcached using.\n *\n * @param {object} a key.\n * @returns a Promise\n */\nfunction get(key) {\n  return new Promise((resolve, reject) => {\n    memcached.get(key, (err, value) => {\n      if (err) {\n        return reject(err);\n      }\n\n      if (!value) {\n        return reject('Not Found. Key: ' + key);\n      }\n\n      return resolve(value);\n    });\n  });\n}\n\n/**\n * Deletes a value from memcached.\n *\n * @param {object} a key.\n * @returns a Promise\n */\nfunction del(key) {\n  return new Promise((resolve, reject) => {\n    memcached.del(key, (err, value) => {\n      if (err) {\n        return reject(err);\n      }\n\n      if (!value) {\n        return reject('Not Found. Key: ' + key);\n      }\n\n      return resolve(value);\n    });\n  });\n}\n\n/**\n * Sets a value in Memcached.\n *\n * @param {object} the key.\n * @param {object} the value.\n * @param {Number} a lifetime.\n * @returns a Promise\n */\nfunction set(key, value, lifetime) {\n  return new Promise((resolve, reject) => {\n    memcached.set(key, value, lifetime, err => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve();\n    });\n  });\n}\n\n/**\n * Replaces a value in Memcached.\n *\n * @param {object} the key.\n * @param {object} the value.\n * @param {Number} a lifetime.\n * @returns a Promise\n */\nfunction replace(key, value, lifetime) {\n  return new Promise((resolve, reject) => {\n    memcached.replace(key, value, lifetime, err => {\n      if (err) {\n        console.error(err);\n        return reject(err);\n      }\n      return resolve();\n    });\n  });\n}\n\nfunction getMulti(keys) {\n  return new Promise((resolve, reject) => {\n    memcached.getMulti(keys, (err, data) => {\n      if (err) {\n        console.error(err);\n        return reject(err);\n      }\n      return resolve(data);\n    });\n  });\n}\n\n/**\n * Flushes memcache\n */\nfunction flush() {\n  memcached.flush();\n}\n\n/**\n * Stores URLs in PAGELIST_URLS that need to be removed from cache.\n */\nfunction storeCachedUrls(url) {\n  // Stores list and search pages\n  if (url.indexOf('/pwas/') === -1 || url.indexOf('/pwas/search') >= 0) {\n    this.get(PAGELIST_URLS)\n      .then(array => {\n        let urlSet = new Set(array);\n        urlSet.add(url);\n        this.set(PAGELIST_URLS, Array.from(urlSet), CACHE_LIFETIME);\n      })\n      .catch(_ => {\n        let urlSet = new Set();\n        urlSet.add(url);\n        this.set(PAGELIST_URLS, Array.from(urlSet), CACHE_LIFETIME);\n      });\n  }\n}\n\n/**\n * Flush URLs from PAGELIST_URLS list.\n */\nfunction flushCacheUrls() {\n  this.get(PAGELIST_URLS)\n    .then(array => {\n      array.forEach(url => {\n        this.del(url);\n      });\n      this.del(PAGELIST_URLS);\n    })\n    .catch(err => {\n      console.error('Error flushing cache URLs: ', err);\n    });\n}\n\nmodule.exports = {\n  get: get,\n  del: del,\n  set: set,\n  replace: replace,\n  flush: flush,\n  getMulti: getMulti,\n  storeCachedUrls: storeCachedUrls,\n  flushCacheUrls: flushCacheUrls\n};\n"
  },
  {
    "path": "lib/data-fetcher.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst URL = require('url');\nconst fetch = require('node-fetch');\nconst fs = require('fs');\nconst cheerio = require('cheerio');\nconst config = require('../config/config');\n\nconst FIREBASE_AUTH = config.get('FIREBASE_AUTH');\nconst USER_AGENT = ['Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36',\n  '(KHTML, like Gecko) Chrome/48.0.2564.23 Mobile Safari/537.36'].join(' ');\n\n/**\n * Fetches the description from a webpage's metadata.\n *\n * @param {string} url of the page to get the description from\n * @return {Promise<string>} with the description or error\n */\nfunction fetchMetadataDescription(url) {\n  return fetchWithUA(url)\n    .then(response => response.text())\n    .then(html => cheerio.load(html))\n    .then($ => {\n      return $('meta[name=\"description\"]').attr('content');\n    });\n}\n\n/**\n * Fetches the manifest URL from a webpage's link rel header.\n *\n * @param {string} url of the page to get the manifest link from\n * @return {Promise<string>} with the URL or error\n */\nfunction fetchLinkRelManifestUrl(pageUrl) {\n  return fetchWithUA(pageUrl)\n    .then(response => response.text())\n    .then(html => cheerio.load(html))\n    .then($ => $('link[rel=\"manifest\"]').attr('href'))\n    .then(newUrl => {\n      if (!newUrl) {\n        return Promise.reject(\n          'this Web does not have a Web App Manifest (<link rel=\"manifest\" href=\"...\">)');\n      }\n      return URL.resolve(pageUrl, newUrl);\n    });\n}\n\n/**\n * Fetches a URL using the USER_AGENT set on top of this file.\n * Uses spdy for http2 support\n *\n * @param {string} url to te be fetched\n * @return {Promise<Response>}\n */\nfunction fetchWithUA(url) {\n  const options = {\n    method: 'GET',\n    headers: {\n      'user-agent': USER_AGENT\n    },\n    timeout: 1000\n  };\n  return fetch(url, options);\n}\n\n/**\n * Fetches a URL using the USER_AGENT set on top of this file and returns Json.\n *\n * @param {string} url to te be fetched\n * @return {Promise<Json>}\n */\nfunction fetchJsonWithUA(url) {\n  return fetchWithUA(url)\n    .then(response => response.json());\n}\n\n/**\n * Reads a file and returns a promise instead of the fs' callback.\n *\n * @param {string} filename to te be read\n * @return {Promise<data>} with the content of the file\n */\nfunction readFile(filename) {\n  return new Promise((resolve, reject) => {\n    fs.readFile(filename, {encoding: 'utf-8'}, (err, data) => {\n      if (err) {\n        reject(err);\n      } else {\n        resolve(data);\n      }\n    });\n  });\n}\n\nfunction _firebaseOptions(payload) {\n  if (payload) {\n    return {\n      method: 'POST',\n      headers: {\n        'Authorization': FIREBASE_AUTH,\n        'content-type': 'application/json'\n      },\n      body: JSON.stringify(payload)\n    };\n  }\n\n  return {\n    method: 'GET',\n    headers: {\n      Authorization: FIREBASE_AUTH\n    }\n  };\n}\n\nfunction _handleFirebaseResponse(response) {\n  // Request was successful. Resolve Promise with the JSON.\n  if (response.status === 200) {\n    return response.json();\n  }\n\n  // Request returned an error response. Reject with an error message.\n  return response.text()\n    .then(text => {\n      return Promise.reject(\n        'Request failed with response: ' + response.status + ' Message: ' + text);\n    });\n}\n\nfunction firebaseFetch(url, payload) {\n  const options = _firebaseOptions(payload);\n  const res = fetch(url, options);\n  res.then(r => {\n    if (r.status !== 200) {\n      r.text().then(msg => {\n        // Add codebase-wide logging system\n        console.warn(`firebaseFetch error: GET ${url} => ${r.status}: ${msg}`);\n      });\n    }\n  });\n  return res.then(_handleFirebaseResponse);\n}\n\n/**\n * POST to url\n *\n * @param {string} url to POST to\n * * @param {string} body of the POST\n * @return {Promise<Response>}\n */\nfunction postJson(url, body) {\n  return fetch(url, {\n    method: 'POST',\n    headers: {'Content-Type': 'application/json'},\n    body: JSON.stringify(body)});\n}\n\nmodule.exports = {\n  fetchMetadataDescription,\n  fetchLinkRelManifestUrl,\n  fetchWithUA,\n  fetchJsonWithUA,\n  firebaseFetch,\n  _firebaseOptions,\n  _handleFirebaseResponse,\n  readFile,\n  postJson\n};\n"
  },
  {
    "path": "lib/event-bus.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst EventEmitter = require('events');\nconst messageBus = new EventEmitter();\nmodule.exports = messageBus;\n"
  },
  {
    "path": "lib/favorite-pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst db = require('../lib/model-datastore');\nconst FavoritePwa = require('../models/favorite-pwa');\n\nconst ENTITY_NAME = 'FavoritePwa';\n\n/**\n * Saves a FavoritePwa object into the DB.\n *\n * @param {FavoritePwa} lighthouse\n * @return {Promise<FavoritePwa>}\n */\nexports.save = function(favoritePwa) {\n  return db.update(ENTITY_NAME, favoritePwa.id, favoritePwa)\n    .catch(err => {\n      console.log(err);\n      return Promise.reject('Error saving the FavoritePwa');\n    });\n};\n\n/**\n * Retrieves FavoritePwas for a given User.\n *\n * @param {number} userId\n * @return {Promise<Array<FavoritePwa>>}\n */\nexports.findByUserId = function(userId) {\n  console.log(userId);\n  const query = db.createQuery(ENTITY_NAME).filter('userId', '=', userId);\n  return db.runQuery(query).then(result => {\n    if (!result || result.entities.length === 0) {\n      return null;\n    }\n    let favoritePwas = result.entities.map(entry => {\n      return new FavoritePwa(entry.pwaId, entry.userId);\n    });\n    return favoritePwas;\n  });\n};\n\n/**\n * Retrieves a FavoritePwa for given User & PWA.\n *\n * @param {number} pwaId\n * @param {number} userId\n * @return {Promise<FavoritePwa>}\n */\nexports.findFavoritePwa = function(pwaId, userId) {\n  const query = db.createQuery(ENTITY_NAME).filter('pwaId', '=', parseInt(pwaId, 10))\n      .filter('userId', '=', userId).limit(1);\n  return db.runQuery(query).then(result => {\n    if (!result || result.entities.length === 0) {\n      return null;\n    }\n    return new FavoritePwa(result.entities[0].pwaId, result.entities[0].userId);\n  });\n};\n\n/**\n * Deletes a FavoritePwa from DB.\n *\n * @param {number} key of the FavoritePwa\n * @return {Promise<>}\n */\nexports.delete = function(key) {\n  return db.delete(ENTITY_NAME, key);\n};\n"
  },
  {
    "path": "lib/images.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\nconst dataFetcher = require('../lib/data-fetcher');\nconst sharp = require('sharp');\nconst stream = require('stream');\nconst strongDataUri = require('strong-data-uri');\nconst url = require('url');\nconst mime = require('mime-types');\n\nconst cloudStorage = require('@google-cloud/storage');\nconst CLOUD_BUCKET = config.get('CLOUD_BUCKET');\nconst storage = cloudStorage({\n  projectId: config.get('GCLOUD_PROJECT')\n});\nconst bucket = storage.bucket(CLOUD_BUCKET);\n\nconst CACHE_CONTROL_EXPIRES = 60 * 60 * 24; // 1 day.\n\n/**\n * Fetches and Saves an Image to Google Cloud Storage.\n *\n * @param {string} url image URL to retrieve\n * @param {string} destFile name of the destination file\n * @return {Promise<url[]>>} URLs for the new images in Google Cloud Storage\n */\nfunction fetchAndSave(imageUrl, destFile) {\n  const parsedUrl = url.parse(imageUrl);\n  switch (parsedUrl.protocol) {\n    case 'data:': {\n      return this.dataUriAndSave(imageUrl);\n    }\n    case 'http:':\n    case 'https:': {\n      return dataFetcher.fetchWithUA(imageUrl)\n        .then(response => {\n          if (response.status !== 200) {\n            return Promise.reject(new Error(\n              'Bad Response (' + response.status + ') loading image: ' + response.url));\n          }\n\n          // Using mime.lookup insteand of `// response.headers.get('Content-Type');`, as some\n          // publishers use an invalid value for the content-type.\n          const contentType = mime.lookup(imageUrl);\n          return this.saveImages(response.body, destFile, contentType);\n        })\n        .then(savedUrls => {\n          return savedUrls;\n        });\n    }\n    default: {\n      return Promise.reject('Unsupported Protocol: ' + parsedUrl.protocol);\n    }\n  }\n}\n\n/**\n * Process a Data URI Image and Saves to Google Cloud Storage.\n *\n * @param {string} url Data URI image URL to process\n * @param {string} destFile name of the destination file\n * @return {Promise<url[]>} URLs for the new images in Google Cloud Storage\n */\nfunction dataUriAndSave(url, destFile) {\n  const buffer = strongDataUri.decode(url);\n  const contentType = buffer.mimetype;\n  const bufferStream = new stream.PassThrough();\n  bufferStream.end(buffer);\n  return this.saveImages(bufferStream, destFile, contentType);\n}\n\n/**\n * Saves the content from the stream to Google Cloud Storage\n * with 3 difference sizes, original, 128*128px and 64*64px\n *\n * @param {stream.Readable} stream\n * @param {string} destFile name of the destination file\n * @param {contentType} destFile image's mimetype\n * @return {Promise<url[]>} URLs for the new images in Google Cloud Storage\n */\nfunction saveImages(readStream, destFile, contentType) {\n  return Promise.all([\n    this.saveImage(readStream, destFile, contentType),\n    this.saveImage(readStream, destFile, contentType, 128),\n    this.saveImage(readStream, destFile, contentType, 64)\n  ])\n  .catch(err => {\n    console.log('Error Saving Images', err);\n    return null;\n  });\n}\n\n/**\n * Saves the content from the stream to Google Cloud Storage.\n *\n * @param {stream.Readable} stream\n * @param {string} destFile name of the destination file\n * @param {contentType} destFile image's mimetype\n * @param {int} size image's new size\n * @return {Promise<string>} full public URL of saved image in Google Cloud Storage\n */\nfunction saveImage(readStream, destFile, contentType, size) {\n  const destFilename = (size || 'original') + '_' + destFile;\n  return new Promise((resolve, reject) => {\n    const file = bucket.file(destFilename);\n    const metadata = {\n      contentType: contentType,\n      cacheControl: 'public, max-age=' + CACHE_CONTROL_EXPIRES\n    };\n\n    const writeStream = file.createWriteStream({\n      metadata: metadata,\n      gzip: 'auto' // Enables Gzipping the content based on the contentType.\n    });\n\n    writeStream.on('error', err => {\n      reject(err);\n    });\n\n    writeStream.on('finish', () => {\n      resolve(getPublicUrl(destFilename));\n    });\n\n    if (size) {\n      const transformer = sharp().resize(size);\n      transformer.on('error', err => {\n        reject(err);\n      });\n      readStream.pipe(transformer).pipe(writeStream);\n    } else {\n      readStream.pipe(writeStream);\n    }\n  });\n}\n\n/**\n * @private\n * Given a filename, returns the GCloud address for it.\n *\n * @param {string} filename the original filename.\n * @return {Promise<string>} the public URL of the file.\n */\nfunction getPublicUrl(filename) {\n  return 'https://storage.googleapis.com/' + CLOUD_BUCKET + '/' + filename;\n}\n\nmodule.exports = {\n  fetchAndSave,\n  saveImage,\n  saveImages,\n  dataUriAndSave\n};\n"
  },
  {
    "path": "lib/lighthouse.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\nconst pwaLib = require('../lib/pwa');\nconst libWebPerformance = require('../lib/web-performance');\nconst Lighthouse = require('../models/lighthouse');\n\nconst db = require('../lib/model-datastore');\nconst datastore = require('@google-cloud/datastore');\nconst ds = datastore({\n  projectId: config.get('GCLOUD_PROJECT')\n});\n\nconst ENTITY_NAME = 'Lighthouse';\nconst E_PWA_NOT_FOUND = exports.E_PWA_NOT_FOUND = 1;\nconst E_FETCHING_STORING_LIGHTHOUSE = exports.E_FETCHING_STORING_LIGHTHOUSE = 2;\nconst LIGTHOUSE_DATE_CHANGES = ['2016-12-01', '2017-03-01', '2017-05-05',\n  '2017-05-25', '2017-06-20'];\n\n/**\n * Saves a Lighthouse object into the DB.\n *\n * @param {Lighthouse} lighthouse\n * @return {Promise<Lighthouse>}\n */\nexports.save = function(lighthouse) {\n  return new Promise((resolve, reject) => {\n    db.update(ENTITY_NAME, lighthouse.id, lighthouse)\n      .then(result => {\n        return resolve(result);\n      })\n      .catch(err => {\n        console.log(err);\n        return reject('Error saving the Lighthouse report');\n      });\n  });\n};\n\n/**\n * Retrieves the latest Lighthouse for a PWA.\n *\n * @param {number} pwaId\n * @return {Promise<Lighthouse>}\n */\nexports.findByPwaId = function(pwaId) {\n  return new Promise((resolve, reject) => {\n    const query = ds.createQuery(ENTITY_NAME)\n      .filter('pwaId', '=', parseInt(pwaId, 10)).order('date', {descending: true}).limit(1);\n    ds.runQuery(query, (err, lighthouses) => {\n      if (err) {\n        return reject(err);\n      }\n      if (lighthouses.length === 0) {\n        return resolve(null);\n      }\n      return resolve(lighthouses[0]);\n    });\n  });\n};\n\n/**\n * Retrieves the Lighthouse data for a PWA\n *\n * @param {number} pwaId\n * @return {Promise<Lighthouse[]>}\n */\nexports.getLighthouseByPwaId = function(pwaId) {\n  return new Promise((resolve, reject) => {\n    // Gets the last 2 years of Ligthouses for a PWA\n    const query = ds.createQuery(ENTITY_NAME)\n      .filter('pwaId', '=', parseInt(pwaId, 10)).order('date', {descending: true}).limit(730);\n    ds.runQuery(query, (err, lighthouses) => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve(lighthouses);\n    });\n  });\n};\n\n/**\n * Retrieves the Lighthouse Grpah data for a PWA\n * in Google Charts JSON format\n *\n * @param {number} pwaId\n * @return {Promise<Json>}\n */\nexports.getLighthouseGraphByPwaId = function(pwaId) {\n  // Gets the last 2 years of Ligthouses for a PWA\n  return this.getLighthouseByPwaId(pwaId)\n    .then(lighthouses => {\n      if (lighthouses.length === 0) {\n        return null;\n      }\n      // Graph data uses the Google Charts JSON format:\n      // https://developers.google.com/chart/interactive/docs/reference#dataparam\n      let data = {};\n      data.cols = [{label: 'Date', type: 'date'}, {label: 'Score', type: 'number'},\n        {label: 'LH change', type: 'number'}];\n      data.rows = [];\n      lighthouses.forEach(lighthouse => {\n        let lighthouseChange = null;\n        const date = new Date(Date.parse(lighthouse.date));\n        // Add dots over the line to anotate lighthouse changes\n        if (LIGTHOUSE_DATE_CHANGES.indexOf(lighthouse.date) > -1) {\n          lighthouseChange = lighthouse.totalScore;\n        }\n        data.rows.push(\n          {c: [{v:\n            'Date(' + date.getFullYear() + ',' + date.getMonth() + ',' + date.getDate() + ')'},\n          {v: lighthouse.totalScore},\n          {v: lighthouseChange}]});\n      });\n      return data;\n    });\n};\n\n/**\n * Generates a Lighthouse report for a PWA by its id.\n *\n * @param {number} pwaId\n * @return {Promise<Lighthouse>}\n */\nexports.fetchAndSave = function(pwaId) {\n  return new Promise((resolve, reject) => {\n    pwaLib.find(pwaId)\n      .then(pwa => {\n        libWebPerformance.getLighthouseReport(pwa)\n          .then(lighthouseJson => {\n            const reportData = lighthouseJson[0].rawData.value;\n            const lighthouse = new Lighthouse(pwaId, pwa.absoluteStartUrl, reportData);\n            this.save(lighthouse);\n            return lighthouse;\n          })\n          .then(lighthouse => {\n            return resolve(lighthouse);\n          })\n          .catch(err => {\n            console.error(err);\n            return reject(E_FETCHING_STORING_LIGHTHOUSE);\n          });\n      })\n      .catch(err => {\n        console.error(err);\n        return reject(E_PWA_NOT_FOUND);\n      });\n  });\n};\n\n/**\n * Creates a new JSON with the main elemnts from a Lighthouse report.\n *\n * @param {string} lighthouseJson\n * @return {JSON}\n */\nexports.processLighthouseJson = function(lighthouseJson) {\n  let lighthouseInfo = {};\n  lighthouseInfo.lighthouseVersion = lighthouseJson.lighthouseVersion;\n  // Some PWAs do not have a LH 2.x report yet\n  if (lighthouseInfo.lighthouseVersion.startsWith('1.')) {\n    lighthouseJson.aggregations.forEach(aggregation => {\n      let i = 0;\n      let totalScore = 0;\n      if (aggregation.name === 'Progressive Web App') {\n        let scoreJson = [];\n        aggregation.score.forEach(score => {\n          scoreJson[i++] = {\n            name: score.name,\n            overall: Math.round(score.overall * 100),\n            subItems: JSON.stringify(score.subItems)\n          };\n          totalScore += score.overall;\n        });\n        lighthouseInfo.aggregation = {\n          name: aggregation.name,\n          description: aggregation.description,\n          scores: scoreJson\n        };\n        if (i > 0) {\n          lighthouseInfo.totalScore = Math.round((totalScore / i) * 100);\n        }\n      }\n    });\n    let j = 0;\n    lighthouseInfo.audits = [];\n    for (let key in lighthouseJson.audits) {\n      if (lighthouseJson.audits.hasOwnProperty(key)) {\n        let audit = lighthouseJson.audits[key];\n        lighthouseInfo.audits[j++] = {\n          name: audit.name,\n          description: audit.description,\n          score: audit.score,\n          displayValue: audit.displayValue,\n          optimalValue: audit.optimalValue\n        };\n      }\n    }\n  } else {\n    lighthouseInfo.totalScore = Math.round(lighthouseJson.score);\n    lighthouseInfo.reportCategories = lighthouseJson.reportCategories;\n  }\n  return lighthouseInfo;\n};\n"
  },
  {
    "path": "lib/manifest.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst dataFetcher = require('../lib/data-fetcher');\nconst Manifest = require('../models/manifest');\n\n/**\n * Fetches the Manifest from the manifestUrl.\n *\n * @param {string} manifestUrl\n * @return {Promise<Manifest>}\n */\nfunction fetchManifest(manifestUrl) {\n  return dataFetcher.fetchJsonWithUA(manifestUrl)\n    .then(json => new Manifest(manifestUrl, json));\n}\n\n/**\n * Wrapper for the manifest validator from lighthouse.\n *\n * @param {Manifest} manifest\n * @param {string} manifestUrl URL of manifest itself\n * @param {string} documentUrl URL of document that links to the manifest\n * @return string[] errors found in manifest\n */\nfunction validateManifest(manifest, manifestUrl, documentUrl) {\n  const parse = require('../third_party/manifest-parser.js');\n  const res = parse(manifest, manifestUrl, documentUrl);\n  // Lighthouse annotates the actual elements with validation errors; \"flatten\"\n  // these here.\n  function flatten(obj) {\n    const debugString = obj.debugString ? [obj.debugString] : [];\n    if (typeof obj.value !== 'object') {\n      return debugString;\n    }\n    return Object.keys(obj.value).reduce((acc, k) => {\n      return acc.concat(flatten(obj.value[k]));\n    }, debugString);\n  }\n  return flatten(res);\n}\n\nmodule.exports = {\n  fetchManifest,\n  validateManifest\n};\n"
  },
  {
    "path": "lib/metadata.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\n/**\n * Generates the default metadata from a http request\n */\nmodule.exports.fromRequest = function(req, newUrl) {\n  const host = req.get('host');\n  const url = newUrl || req.protocol + '://' + host + req.originalUrl;\n  const timestamp = new Date().toISOString();\n  const logo = req.protocol + '://' + host + '/favicons/android-chrome-512x512.png';\n  const leader = req.protocol + '://' + host + '/img/pwa-directory-preview.png';\n  const metadata = {\n    url: url,\n    host: host,\n    datePublished: timestamp,\n    dateModified: timestamp,\n    logo: logo,\n    logoWidth: '512',\n    logoHeight: '512',\n    leader: leader,\n    leaderWidth: '2008',\n    leaderHeight: '1386'\n  };\n  return metadata;\n};\n"
  },
  {
    "path": "lib/model-datastore.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\n\nconst datastore = require('@google-cloud/datastore');\nconst ds = datastore({\n  projectId: config.get('GCLOUD_PROJECT')\n});\n\nconst ENTITY_COUNT_KIND = 'counts';\n\n/**\n * Translates from Datastore's entity format to\n * the format expected by the application.\n *\n * Datastore format:\n *   {\n *     key: [kind, id],\n *     data: {\n *       property: value\n *     }\n *   }\n *\n * Application format:\n *   {\n *     id: id,\n *     property: value\n *   }\n *\n * @param {Object} obj\n * @return {Object}\n */\nfunction fromDatastore(obj) {\n  obj.id = obj[datastore.KEY].id;\n  return obj;\n}\n\n/**\n * Translates from the application's format to the datastore's\n * extended entity property format. It also handles marking any\n * specified properties as non-indexed. Does not translate the key.\n *\n * Application format:\n *   {\n *     id: id,\n *     property: value,\n *     unindexedProperty: value\n *   }\n *\n * Datastore extended format:\n *   [\n *     {\n *       name: property,\n *       value: value\n *     },\n *     {\n *       name: unindexedProperty,\n *       value: value,\n *       excludeFromIndexes: true\n *     }\n *   ]\n *\n * @param {Object} obj\n * @param {Array} nonIndexed\n * @return {Array<DBObject>}\n */\nfunction toDatastore(obj, nonIndexed) {\n  nonIndexed = nonIndexed || [];\n  const results = [];\n  Object.keys(obj).forEach(k => {\n    if (obj[k] === undefined) {\n      return;\n    }\n\n    let value;\n    if (obj[k] instanceof Object) {\n      if (nonIndexed.indexOf(k) === -1) {\n        value = deepCopy(obj[k]);\n      } else {\n        // nonIndexed properties need to be stored as Strings\n        value = JSON.stringify(obj[k]);\n      }\n    } else {\n      value = obj[k];\n    }\n\n    results.push({\n      name: k,\n      value: value,\n      excludeFromIndexes: nonIndexed.indexOf(k) !== -1\n    });\n  });\n  return results;\n}\n\nfunction deepCopy(object) {\n  if (!(object instanceof Object)) {\n    return object;\n  }\n\n  if (object instanceof Date) {\n    return object;\n  }\n\n  const clone = Object.assign({}, object);\n  Object.keys(clone).forEach(k => {\n    clone[k] = deepCopy(clone[k]);\n  });\n\n  return clone;\n}\n\n/**\n * Lists all Entities in the Datastore sorted alphabetically by title.\n * The ``limit`` argument determines the maximum amount of results to\n * return per page. The ``token`` argument allows requesting additional\n * pages. The callback is invoked with ``(err, Entities, nextPageToken)``.\n *\n * @param {string} kind\n * @param {number} offset\n * @param {number} limit\n * @param {object} {field:property name, config:sort direction}\n * @return {Promise<Array<Object>>}\n */\nfunction list(kind, offset, limit, sort, filters) {\n  return runQuery(createQuery(kind, offset, limit, sort, filters));\n}\n\n/**\n * Creates a DB query.\n *\n * @param {string} kind\n * @param {number} offset\n * @param {number} limit\n * @param {object} {field:property name, config:sort direction}\n * @return {query}\n */\nfunction createQuery(kind, offset, limit, sort, filters) {\n  const query = ds.createQuery([kind])\n    .offset(offset || 0)\n    .limit(limit);\n  if (sort) {\n    query.order(sort.field, sort.config);\n  }\n  if (filters) {\n    for (let filter of filters) {\n      query.filter(filter.property, filter.operator, filter.value);\n    }\n  }\n  return query;\n}\n\n/**\n * Executes a DB query.\n *\n * @param {query} query\n * @return {Promise<Array<Object>>}\n */\nfunction runQuery(query) {\n  return new Promise((resolve, reject) => {\n    ds.runQuery(query, (err, entities, nextQuery) => {\n      if (err) {\n        return reject(err);\n      }\n      const hasMore = nextQuery.moreResults !== 'NO_MORE_RESULTS';\n      resolve({\n        entities: entities.map(fromDatastore),\n        hasMore: hasMore,\n        endCursor: nextQuery.endCursor\n      });\n    });\n  });\n}\n\n/**\n * Parse the Key to a number if possible\n * @param {object} key\n * @return {object : number}\n */\nfunction parseKey(key) {\n  return isNaN(key) ? key : parseInt(key, 10);\n}\n\nfunction startTransaction(transaction) {\n  return new Promise((resolve, reject) => {\n    transaction.run(err => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve(transaction);\n    });\n  });\n}\n\nfunction commitTransaction(transaction) {\n  return new Promise((resolve, reject) => {\n    transaction.commit(err => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve();\n    });\n  });\n}\n\nfunction rollbackTransaction(transaction) {\n  return new Promise((resolve, reject) => {\n    transaction.rollback(err => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve();\n    });\n  });\n}\n\nfunction transactionGet(transaction, key) {\n  return new Promise((resolve, reject) => {\n    transaction.get(key, (err, entity) => {\n      if (err) {\n        return reject(err);\n      }\n      return resolve(entity);\n    });\n  });\n}\n\nfunction updateCount(transaction, kind, inc) {\n  return new Promise((resolve, reject) => {\n    const countKey = ds.key([ENTITY_COUNT_KIND, kind]);\n    transaction.get(countKey, (err, countEntity) => {\n      if (err) {\n        return reject(err);\n      }\n\n      let count = 0;\n      if (countEntity) {\n        count = countEntity.count;\n      }\n\n      if (inc) {\n        count++;\n      } else {\n        count--;\n      }\n\n      transaction.save({key: countKey, data: {count: count}});\n      return resolve();\n    });\n  });\n}\n\n/**\n * Creates a new Entity or updates an existing Entity with new data. The provided\n * data is automatically translated into Datastore format. The Entity will be\n * queued for background processing.\n *\n * @param {string} kind\n * @param {string} id\n * @param {Object} data\n * @return {Promise<Object>}\n */\nfunction update(kind, id, data) {\n  return new Promise((resolve, reject) => {\n    let key;\n    if (id) {\n      key = ds.key([kind, parseKey(id)]);\n    } else {\n      key = ds.key(kind);\n    }\n\n    const entity = {\n      key: key,\n      data: toDatastore(data, [\n        'description', '_manifest', '_lighthouseJson', 'lighthouseInfo', 'webPageTest'])\n    };\n\n    ds.save(\n      entity,\n      err => {\n        data.id = entity.key.id;\n        if (err) {\n          reject(err);\n          return;\n        }\n        resolve(data);\n      }\n    );\n  });\n}\n\n/**\n * Creates a new Entity or updates an existing Entity with new data. The provided\n * data is automatically translated into Datastore format. The Entity will be\n * queued for background processing.\n *\n * @param {string} kind\n * @param {string} id\n * @param {Object} data\n * @return {Promise<Object>}\n */\nfunction updateWithCounts(kind, id, data) {\n  let key;\n  if (id) {\n    key = ds.key([kind, parseKey(id)]);\n  } else {\n    key = ds.key(kind);\n  }\n\n  const entity = {\n    key: key,\n    data: toDatastore(data, [\n      'description', '_manifest', '_lighthouseJson', 'lighthouseInfo', 'webPageTest'])\n  };\n\n  const transaction = ds.transaction();\n  return startTransaction(transaction)\n    .then(_ => {\n      transaction.save(entity);\n      if (!id) {\n        return updateCount(transaction, kind, true);\n      }\n      return Promise.resolve();\n    })\n    .then(_ => {\n      return commitTransaction(transaction);\n    })\n    .then(_ => {\n      data.id = key.id;\n      return data;\n    })\n    .catch(err => {\n      console.error(err);\n      return rollbackTransaction(transaction)\n        .then(_ => {\n          return Promise.reject(err);\n        });\n    });\n}\n\nfunction count(kind) {\n  return read(ENTITY_COUNT_KIND, kind)\n    .then(entity => {\n      if (!entity) {\n        return 0;\n      }\n      return entity.count || 0;\n    })\n    .catch(_ => {\n      return 0;\n    });\n}\n\n/**\n * Reads an Object of the specified kind and Id from the Datastore.\n *\n * @param {string} kind\n * @param {string} id\n * @return {Promise<Object>}\n */\nfunction read(kind, id) {\n  return new Promise((resolve, reject) => {\n    const key = ds.key([kind, parseKey(id)]);\n    ds.get(key, (err, entity) => {\n      if (err) {\n        return reject(err);\n      }\n      if (!entity) {\n        return reject({\n          code: 404,\n          message: 'Not found'\n        });\n      }\n      resolve(fromDatastore(entity));\n    });\n  });\n}\n\n/**\n * Deletes an Object with the specified kind and Id from the Datastore\n *\n * @param {string} kind\n * @param {string} id\n * @return {Promise<>}\n */\nfunction _deleteWithCounts(kind, id) {\n  const key = ds.key([kind, parseKey(id)]);\n  const transaction = ds.transaction();\n  return startTransaction(transaction)\n    .then(_ => {\n      return transactionGet(transaction, key);\n    })\n    .then(entity => {\n      if (!entity) {\n        return Promise.reject('Trying to delete entity that does not exist: ' + key.id);\n      }\n      transaction.delete(key);\n      return updateCount(transaction, kind, false);\n    })\n    .then(_ => {\n      return commitTransaction(transaction);\n    })\n    .catch(err => {\n      return rollbackTransaction(transaction)\n        .then(_ => {\n          return Promise.reject(err);\n        });\n    });\n}\n\n/**\n * Deletes an Object with the specified kind and Id from the Datastore\n *\n * @param {string} kind\n * @param {string} id\n * @return {Promise<>}\n */\nfunction _delete(kind, id) {\n  return new Promise((resolve, reject) => {\n    const key = ds.key([kind, parseKey(id)]);\n    ds.delete(key, err => {\n      if (err) {\n        return reject(err);\n      }\n      resolve();\n    });\n  });\n}\n\nmodule.exports = {\n  create: (kind, data) => {\n    update(kind, null, data);\n  },\n  count: count,\n  read: read,\n  update: update,\n  delete: _delete,\n  updateWithCounts: updateWithCounts,\n  deleteWithCounts: _deleteWithCounts,\n  list: list,\n  createQuery: createQuery,\n  runQuery: runQuery\n};\n"
  },
  {
    "path": "lib/notifications.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst dataFetcher = require('../lib/data-fetcher');\n\nexports.list = function(token) {\n  if (!token) {\n    return Promise.reject(new Error('Missing token'));\n  }\n\n  const url = 'https://iid.googleapis.com/iid/info/' + token + '?details=true';\n  return dataFetcher.firebaseFetch(url)\n    .then(userDetails => {\n      if (!userDetails || !userDetails.rel || !userDetails.rel.topics) {\n        return Promise.resolve([]);\n      }\n      return Object.keys(userDetails.rel.topics);\n    });\n};\n\nexports.subscribe = function(token, topic) {\n  if (!token) {\n    return Promise.reject(new Error('Missing token'));\n  }\n\n  if (!topic) {\n    return Promise.reject(new Error('Missing topic'));\n  }\n\n  const url = 'https://iid.googleapis.com/iid/v1:batchAdd';\n  const payload = {\n    to: '/topics/' + topic,\n    registration_tokens: [token] // eslint-disable-line camelcase\n  };\n  return dataFetcher.firebaseFetch(url, payload);\n};\n\nexports.unsubscribe = function(token, topic) {\n  if (!token) {\n    return Promise.reject(new Error('Missing token'));\n  }\n\n  if (!topic) {\n    return Promise.reject(new Error('Missing topic'));\n  }\n\n  const url = 'https://iid.googleapis.com/iid/v1:batchRemove';\n  const payload = {\n    to: '/topics/' + topic,\n    registration_tokens: [token] // eslint-disable-line camelcase\n  };\n  return dataFetcher.firebaseFetch(url, payload);\n};\n\nexports.sendPush = function(topic, notification) {\n  if (!topic) {\n    return Promise.reject(new Error('Missing topic'));\n  }\n\n  if (!notification) {\n    return Promise.reject(new Error('Missing notification'));\n  }\n\n  // Require the notification to have a title, at minimum\n  if (!notification.title) {\n    return Promise.reject(new Error('Missing notification title'));\n  }\n  const url = 'https://fcm.googleapis.com/fcm/send';\n  const payload = {\n    to: '/topics/' + topic,\n    notification: notification\n  };\n  return dataFetcher.firebaseFetch(url, payload);\n};\n"
  },
  {
    "path": "lib/promise-sequential.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\n/** Execute a list of Promise return functions serially\n * @param {list} a list of promise returning functions to execute serially\n * @return {Promise<result>} the result of the last promise in the list\n *  Example:\n *    promiseSequential.all([\n *      _ => this.function1(result),\n *      result => this.function2(result),\n *      result => this.function3(result)\n *    ]);\n */\nexports.all = function(promiseList) {\n  return promiseList.reduce((promiseFn, fn) => {\n    return promiseFn.then(fn);\n  }, Promise.resolve());\n};\n"
  },
  {
    "path": "lib/pwa-index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst libSearch = require('../lib/search');\nconst libPwa = require('../lib/pwa');\nconst Pwa = require('../models/pwa');\nconst db = require('../lib/model-datastore');\nconst ENTITY_NAME = 'PWA';\n\n/**\n * Add all PWAs from the DB into the text search index.\n */\nexports.indexAllPwas = _ => {\n  let indexPage = (skip, limit) =>\n    db.list(ENTITY_NAME, skip, limit)\n      .then(result => {\n        const pwas = result.entities.map(pwa => {\n          return Object.assign(new Pwa(), pwa);\n        });\n        libSearch.addPwas(pwas);\n        if (result.hasMore) {\n          return indexPage(skip + limit, limit);\n        }\n        console.log('All PWAs indexed');\n        return Promise.resolve();\n      });\n  return indexPage(0, 100);\n};\n\n/**\n * Search for PWAS using the text search index.\n *\n * @param {string} query\n * @return {resultPage} resultPage with an arrays of PWAs and hasMore boolean\n */\nexports.searchPwas = string => {\n  return libSearch.search(string).then(result => {\n    let pwas = new Array(result.length);\n    let find = (currentValue, index) => {\n      return libPwa.find(currentValue.ref).then(pwa => {\n        // Inserting at index to keep result order\n        // because Promises run in parallel\n        pwas[index] = pwa;\n      });\n    };\n    return Promise.all(result.map(find)).then(_ => {\n      // Returning all results without pagination for now\n      const resultPage = {\n        pwas: pwas,\n        hasMore: false\n      };\n      return Promise.resolve(resultPage);\n    });\n  });\n};\n\n/**\n * Update PWA in the search index.\n *\n * @param {Pwa} pwa to update\n * @return {Promise<Pwa>}\n */\nexports.updateSearchIndex = function(pwa) {\n  if (pwa.isNew()) {\n    libSearch.addPwa(pwa);\n  } else {\n    libSearch.updatePwa(pwa);\n  }\n  return Promise.resolve(pwa);\n};\n"
  },
  {
    "path": "lib/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst path = require('path');\nconst url = require('url');\nconst config = require('../config/config');\n\nconst libImages = require('../lib/images');\nconst dataFetcher = require('../lib/data-fetcher');\nconst libWebPerformance = require('../lib/web-performance');\nconst libManifest = require('../lib/manifest');\nconst notificationsLib = require('../lib/notifications');\nconst promiseSequential = require('../lib/promise-sequential');\nconst libPwa = require('./pwa');\nconst libPwaIndex = require('./pwa-index');\nconst libCache = require('../lib/data-cache');\n\nconst Pwa = require('../models/pwa');\n\nconst db = require('../lib/model-datastore');\nconst datastore = require('@google-cloud/datastore');\nconst ds = datastore({\n  projectId: config.get('GCLOUD_PROJECT')\n});\n\nconst DEFAULT_SORT_TYPE_KEY = 'score';\nconst ENTITY_NAME = 'PWA';\nconst E_MANIFEST_INVALID_URL = exports.E_MANIFEST_INVALID_URL = 2;\nconst E_MANIFEST_URL_MISSING = exports.E_MANIFEST_URL_MISSING = 3;\nconst E_MISSING_USER_INFORMATION = exports.E_MISSING_USER_INFORMATION = 4;\nconst E_NOT_A_PWA = exports.E_NOT_A_PWA = 5;\n// Waiting time to fetch external info and send notification for new PWAs\nconst WAIT_TIME_NEW_PWAS = 10 * 60 * 1000; // 10 minutes\n\nconst SORT_TYPE_MAP = new Map([\n  ['name', {name: 'name', field: 'manifest.name'}],\n  ['newest', {name: 'newest', field: 'created', config: {descending: true}}],\n  ['score', {name: 'score', field: 'lighthouseScore', config: {descending: true}}]\n]);\n\n/**\n * List of PWAs.\n *\n * @param {number} skip specifies the starting point for handling pagination\n * @param {number} limit number of results to return\n * @param {string} sort the field name to sort the results\n * @return {resultPage} resultPage with an arrays of PWAs and hasMore boolean\n */\nexports.list = async (skip, limit, sort, filters) => {\n  filters = filters || {};\n  let sortType = SORT_TYPE_MAP.get(DEFAULT_SORT_TYPE_KEY);\n  if (sort) {\n    sortType = SORT_TYPE_MAP.get(sort) || sortType;\n  }\n\n  const queryFilters = [];\n  if (filters.minLighthouseScore) {\n    queryFilters.push({\n      property: 'lighthouseScore',\n      operator: '>=',\n      value: filters.minLighthouseScore\n    });\n  }\n\n  if (filters.installable) {\n    queryFilters.push({\n      property: 'installable',\n      operator: '=',\n      value: filters.installable\n    });\n  }\n\n  const result = await db.list(ENTITY_NAME, skip, limit, sortType, queryFilters);\n  const pwas = result.entities.map(pwa => {\n    return Object.assign(new Pwa(), pwa);\n  });\n\n  const resultPage = {\n    pwas: pwas,\n    hasMore: result.hasMore\n  };\n\n  return resultPage;\n};\n\n/**\n * Saves a PWA to DB.\n *\n * @param {Pwa} the Pwa to be saved.\n * @return a Promise.\n */\nexports.savePwa = function(pwa) {\n  return db.updateWithCounts(ENTITY_NAME, pwa.id, pwa)\n    .then(savedPwa => {\n      return savedPwa;\n    })\n    .catch(err => {\n      console.log('Error saving PWA err' + pwa.id);\n      Promise.reject(err);\n    });\n};\n\n/**\n * Finds a PWA by key.\n *\n * @param {number} key of the PWA\n * @return {Pwa} the PWA from DB\n */\nexports.find = function(key) {\n  return db.read(ENTITY_NAME, key)\n    .then(pwa => {\n      const pwaInstance = Object.assign(new Pwa(), pwa);\n      return pwaInstance;\n    });\n};\n\n/**\n * Finds a PWA by its manifest URL from DB.\n *\n * @param {string} manifestUrl of the PWA's manifest\n * @return {Pwa|null} the PWA from DB, or null if not found\n */\nexports.findByManifestUrl = function(manifestUrl) {\n  return new Promise((resolve, reject) => {\n    const query = ds.createQuery(ENTITY_NAME).filter('manifestUrl', manifestUrl);\n    ds.runQuery(query, (err, pwas) => {\n      if (err) {\n        return reject(err);\n      }\n\n      if (pwas.length === 0) {\n        return resolve(null);\n      }\n\n      let pwa = Object.assign(new Pwa(), pwas[0]);\n      return resolve(pwa);\n    });\n  });\n};\n\n/**\n * Finds a PWA by its encodedStartUrl from DB.\n *\n * @param {string} encodedStartUrl of the PWA's manifest\n * @return {Pwa|null} the PWA from DB, or null if not found\n */\nexports.findByEncodedStartUrl = function(encodedStartUrl) {\n  return new Promise((resolve, reject) => {\n    const query = ds.createQuery(ENTITY_NAME).filter('encodedStartUrl', encodedStartUrl);\n    ds.runQuery(query, (err, pwas) => {\n      if (err) {\n        return reject(err);\n      }\n\n      if (pwas.length === 0) {\n        return resolve(null);\n      }\n\n      let pwa = Object.assign(new Pwa(), pwas[0]);\n      return resolve(pwa);\n    });\n  });\n};\n\n/**\n * Creates or Updates a PWA.\n *\n * Steps:\n *  1) Validate Pwa\n *  2) Update Pwa's Manifest\n *  3) Save (to get the DB id for following steps)\n *  4) Update PWA's MetadataDescription\n *  5) Update PWA's Icon\n *  6) Save\n *  7) (in background):\n *    a) Submit PWA for WebPerformance info\n *    b) Get Pwa Performance info\n *    c) Delete modified PWAs from cache\n *\n * @param {Pwa} pwa to update\n * @return {Pwa} the updated PWA\n */\nexports.createOrUpdatePwa = function(pwa) {\n  return promiseSequential.all([\n    _ => (pwa),\n    this.validatePwa,\n    this.updatePwaManifest,\n    this.savePwa,\n    this.updatePwaIcon,\n    this.savePwa,\n    this.removePwaFromCache,\n    savedPwa => {\n      // In background\n      libPwaIndex.updateSearchIndex(savedPwa);\n      this.submitWebPageUrlForWebPerformanceInformation(savedPwa);\n      this.getPwaPerformanceInfo(savedPwa);\n      return savedPwa;\n    }\n  ]);\n};\n\n/**\n * Get Pwa Performance info\n *\n * @param {Pwa} pwa to update\n * @return {Promise<Array>}\n */\nexports.getPwaPerformanceInfo = function(pwa) {\n  let timeout = pwa.isNew() ? WAIT_TIME_NEW_PWAS : 0;\n  setTimeout(_ => {\n    return promiseSequential.all([\n      _ => (pwa),\n      this.updatePwaMetadataDescription,\n      this.updatePwaLighthouseInfo,\n      this.updatePwaPageSpeedInformation,\n      this.updatePwaWebPageTestInformation,\n      this.savePwa,\n      this.removePwaFromCache,\n      this.sendNewAppNotification\n    ]);\n  }, timeout);\n};\n\n/**\n * Remove PWA from cache\n *\n * @param {Pwa} pwa to remove\n * @return {Promise<Pwa>}\n */\nexports.removePwaFromCache = function(pwa) {\n  if (pwa.isNew()) {\n    libCache.flushCacheUrls();\n  }\n  // Delete modified PWA from cache\n  const url = '/pwas/' + pwa.id;\n  libCache.del(url)\n    .catch(err => {\n      console.error(`Error removing ${url} from memcached`, err.message);\n    });\n  libCache.del(url + '?contentOnly=true')\n    .catch(err => {\n      console.error(`Error removing ${url} from memcached`, err.message);\n    });\n  return Promise.resolve(pwa);\n};\n\n/**\n * Validates PWA's data\n *\n * @param {Pwa} pwa to validate\n * @return {Promise<Pwa>} Promise with validated PWA or rejects with error\n */\nexports.validatePwa = function(pwa) {\n  if (!pwa || !(pwa instanceof Pwa)) {\n    return Promise.reject(E_NOT_A_PWA);\n  }\n  if (!pwa.manifestUrl) {\n    return Promise.reject(E_MANIFEST_URL_MISSING);\n  }\n  const manifestUrl = url.format(pwa.manifestUrl);\n  if (!(manifestUrl.startsWith('http://') ||\n        manifestUrl.startsWith('https://'))) {\n    return Promise.reject(E_MANIFEST_INVALID_URL);\n  }\n  if (!pwa.user || !pwa.user.id) {\n    return Promise.reject(E_MISSING_USER_INFORMATION);\n  }\n  return Promise.resolve(pwa);\n};\n\n/**\n * Fetches the manifest for a PWA using it's manifest URL\n * or the webpage's link rel=manifest\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Manifest>} with the manifest for the PWA\n */\nexports.fetchManifest = function(pwa) {\n  return libManifest.fetchManifest(pwa.manifestUrl)\n    .then(manifest => {\n      return manifest;\n    })\n    .catch(_ => {\n      // if there is not a manifest in the pwa.manifestUrl\n      // we check if it is a webpage with a link rel=manifest to the manifest\n      return dataFetcher.fetchLinkRelManifestUrl(pwa.manifestUrl)\n        .then(newManifestUrl => {\n          // remove hash from url\n          pwa.manifestUrl = newManifestUrl.replace(/#.*/, '');\n          return libManifest.fetchManifest(newManifestUrl);\n        })\n        .catch(err => {\n          console.log('Error while fetching the PWA manifest ' + err);\n          return Promise.reject(err);\n        });\n    });\n};\n\n/**\n * Update PWA's Manifest.\n *\n * @param {Pwa} Pwa to update\n * @return {Pwa} the updated PWA\n */\nexports.updatePwaManifest = function(pwa) {\n  return libPwa.fetchManifest(pwa)\n    .then(manifest => {\n      return libPwa.findByManifestUrl(pwa.manifestUrl)\n        .then(existingPwa => {\n          if (existingPwa) {\n            pwa = existingPwa;\n            pwa.updated = new Date();\n          }\n          const validationErrors = libPwa.validateManifest(pwa, manifest);\n          if (validationErrors.length > 0) {\n            return Promise.reject('Error while validating the manifest: ' + validationErrors);\n          }\n          pwa.manifest = manifest;\n\n          // Creates a encodedStartUrl for human readable URLs\n          pwa.generateEncodedStartUrl();\n\n          return pwa;\n        });\n    });\n};\n\n/**\n * Validate PWA's Manifest.\n *\n * @param {Pwa} Pwa to validate\n * @param {Manifest} Manifest to validate\n * @returns {errors[]} Return errors in an array\n */\nexports.validateManifest = function(pwa, manifest) {\n  return libManifest.validateManifest(\n    manifest.raw, pwa.manifestUrl, pwa.absoluteStartUrl);\n};\n\n/**\n * Sends a push notification for new PWAs using Firebase Cloud Messaging.\n *\n * @param {Pwa} pwa to send the notification for\n * @return {Promise<Pwa>} with the notified PWA\n */\nexports.sendNewAppNotification = function(pwa) {\n  if (!pwa.isNew()) {\n    return Promise.resolve(pwa);\n  }\n  console.log('Sending Notification for ', pwa.id);\n  const clickAction = config.get('CANONICAL_ROOT') + 'pwas/' + pwa.id + '?utm_source=push';\n  return notificationsLib.sendPush('new-apps', {\n    title: pwa.name + ' added to PWA Directory',\n    body: pwa.description || '',\n    icon: pwa.iconUrl64 || '',\n    click_action: clickAction // eslint-disable-line camelcase\n  })\n  .then(_ => {\n    return pwa;\n  })\n  .catch(err => {\n    console.log('Error while sending PWA Notification ' + err);\n    return pwa;\n  });\n};\n\n/**\n * Deletes a PWA from DB.\n *\n * @param {number} key of the PWA\n * @return {Promise<>}\n */\nexports.delete = function(key) {\n  return db.delete(ENTITY_NAME, key);\n};\n\n/**\n * Updates the description from the webpage's metadata if not present in the Manifest.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.updatePwaMetadataDescription = function(pwa) {\n  return dataFetcher.fetchMetadataDescription(pwa.absoluteStartUrl)\n    .then(metaDescription => {\n      pwa.metaDescription = metaDescription;\n      console.log('Updated PWA MetadataDescription: ', pwa.id);\n      return pwa;\n    })\n    .catch(err => {\n      console.log('Error while updating PWA MetadataDescription ' + err);\n      return pwa;\n    });\n};\n\n/**\n * Updates the main icon of a PWA.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.updatePwaIcon = function(pwa) {\n  const bestIconUrl = pwa.manifest.getBestIconUrl();\n  if (!bestIconUrl) {\n    console.log('bestIconUrl is null');\n    return Promise.resolve(pwa);\n  }\n  const extension = path.extname(url.parse(bestIconUrl).pathname);\n  const bucketFileName = pwa.id + extension;\n  return libImages.fetchAndSave(bestIconUrl, bucketFileName)\n    .then(savedUrls => {\n      pwa.iconUrl = savedUrls[0];\n      pwa.iconUrl128 = savedUrls[1];\n      pwa.iconUrl64 = savedUrls[2];\n      console.log('Updated PWA Icon/Image: ', pwa.id);\n      return pwa;\n    })\n    .catch(err => {\n      console.error('Error while updating PWA Icon/Image ' + err);\n      return pwa;\n    });\n};\n\n/**\n * Submit WebPageUrl to WebPerformance service,\n * that service runs daily WebPageTest, PageSpeed and Lighthouse.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.submitWebPageUrlForWebPerformanceInformation = function(pwa) {\n  return libWebPerformance.submitWebPageUrl(pwa)\n    .then(result => {\n      if (result.status === 200) {\n        console.log('Submited PWA for WebPerformance info: ', pwa.id);\n      } else {\n        console.log('Error while submiting PWA for WebPerformance information: ' +\n          pwa.id + ' ' + JSON.stringify(result));\n      }\n      return pwa;\n    })\n    .catch(err => {\n      console.log(\n        'Error while submiting PWA for WebPerformance information: ' + pwa.id + ' ' + err);\n      return pwa;\n    });\n};\n\n/**\n * Updates the Lighthouse information.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.updatePwaLighthouseInfo = function(pwa) {\n  return libWebPerformance.getLighthouseReport(pwa)\n    .then(lighthouseJson => {\n      // We are not using the full rawData or rawDataBlob report anymore\n      delete lighthouseJson[0].rawData;\n      delete lighthouseJson[0].rawDataBlob;\n      pwa.lighthouseScore = Math.round(lighthouseJson[0].pwaScore);\n      pwa.lighthouse = lighthouseJson[0];\n      pwa.installable = lighthouseJson[0].installable;\n      console.log('Updated PWA Lighthouse info for: ', pwa.id);\n      return pwa;\n    })\n    .catch(err => {\n      console.log('Error while updating PWA Lighthouse information ' + err);\n      return pwa;\n    });\n};\n\n/**\n * Update PageSpeed information.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.updatePwaPageSpeedInformation = function(pwa) {\n  return libWebPerformance.getPageSpeedReport(pwa)\n    .then(pageSpeedJson => {\n      console.log('Updated PWA PageSpeed info: ', pwa.id);\n      pwa.pageSpeed = pageSpeedJson[0];\n      return pwa;\n    })\n    .catch(err => {\n      console.log('Error while updating PageSpeed information: ' + pwa.id + ' ' + err);\n      return pwa;\n    });\n};\n\n/**\n * Update WebPageTest information.\n *\n * @param {Pwa} the PWA to update\n * @return {Promise<Pwa>} with the updated PWA\n */\nexports.updatePwaWebPageTestInformation = function(pwa) {\n  return libWebPerformance.getWebPageTestReport(pwa)\n    .then(json => {\n      console.log('Updated PWA WebPageTest info: ', pwa.id);\n      let webPageTestJson = json[0];\n      // remove rawFirstViewData to make the field smaller than 1500 bytes\n      webPageTestJson.rawFirstViewData = null;\n      pwa.webPageTest = webPageTestJson;\n      return pwa;\n    })\n    .catch(err => {\n      console.log('Error while updating WebPageTest information: ' + pwa.id + ' ' + err);\n      return pwa;\n    });\n};\n\nexports.count = function() {\n  return db.count(ENTITY_NAME);\n};\n"
  },
  {
    "path": "lib/search.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst URL = require('url');\nconst elasticlunr = require('elasticlunr');\nconst libCache = require('../lib/data-cache');\nconst libPwaIndex = require('../lib/pwa-index');\n\nconst CACHE_LIFETIME = 60 * 60 * 24 * 7; // 7 days\nconst SEARCH_INDEX_CHANGE = 'SearchIndexChange';\n\n/**\n * Search class for the elasticlunr functions\n *\n * Exports a singleton object instance\n */\nclass Search {\n\n  constructor() {\n    this._initIndex();\n  }\n\n  _initIndex() {\n    this._index = elasticlunr(function() {\n      this.setRef('id');\n      this.addField('displayName');\n      this.addField('urlText');\n    });\n    this._modified = new Date();\n  }\n\n  /**\n   * Create a doc element from a PWA.\n   *\n   * @param {PWA} PWA to index\n   * @return {doc} a doc for the text search engine\n   */\n  _docFromPwa(pwa) {\n    const url = URL.parse(pwa.absoluteStartUrl);\n    const urlText = url.hostname.replace(/\\.|-/g, ' ');\n    return {\n      id: pwa.id,\n      displayName: pwa.displayName,\n      urlText: urlText\n    };\n  }\n\n  /**\n   * Add a PWA to the search index.\n   *\n   * @param {PWA} PWA to index\n   * @return {Promise<doc>} the doc added to the text search engine\n   */\n  addPwa(pwa) {\n    const doc = this._addPwa(pwa);\n    this.sarchIndexChange();\n    return Promise.resolve(doc);\n  }\n\n  /**\n   * Add a list of PWA to the search index.\n   *\n   * @param {PWA[]} PWAs to index\n   * @return {Promise<>}\n   */\n  addPwas(pwas) {\n    pwas.forEach(pwa => this._addPwa(pwa));\n    this.sarchIndexChange();\n    return Promise.resolve();\n  }\n\n  _addPwa(pwa) {\n    const doc = this._docFromPwa(pwa);\n    this._index.addDoc(doc);\n    return doc;\n  }\n\n  /**\n   * Update a PWA on the search index.\n   *\n   * @param {PWA} PWA to update\n   * @return {Promise<doc>} the doc updated on the text search engine\n   */\n  updatePwa(pwa) {\n    const doc = this._docFromPwa(pwa);\n    this._index.updateDoc(doc);\n    this.sarchIndexChange();\n    return Promise.resolve(doc);\n  }\n\n  /**\n   * Remove a PWA from the search index.\n   *\n   * @param {PWA} PWA to remove\n   * @return {Promise<doc>} the doc removed from the text search engine\n   */\n  removePwa(pwa) {\n    const doc = this._docFromPwa(pwa);\n    this._index.removeDoc(doc);\n    this.sarchIndexChange();\n    return Promise.resolve(doc);\n  }\n\n  /**\n   * Search the text index.\n   *\n   * @param {string} query\n   * @return {Promise<json>} with the matching PWA Ids and scores\n   *\n   * [{\n   *    \"ref\": 123456789,\n   *    \"score\": 0.5376053707962494\n   *  },\n   *  {\n   *    \"ref\": 456789012,\n   *    \"score\": 0.5237481076838757\n   * }]\n   */\n  search(string) {\n    const options = {expand: true};\n    const result = this._index.search(string, options);\n    // Update the search index for the next query\n    this.checkForSearchIndexChange();\n    return Promise.resolve(result);\n  }\n\n  /**\n   * Record a change in the search index in memcached.\n   *\n   * @param {date} optional date\n   */\n  sarchIndexChange(date = new Date()) {\n    libCache.set(SEARCH_INDEX_CHANGE, date, CACHE_LIFETIME)\n      .catch(err => console.error('sarchIndexChange error', err.message));\n  }\n\n  /**\n   * Check of the latest change in the search index and updete if needed.\n   *\n   * @param {date} optional date\n   */\n  checkForSearchIndexChange() {\n    libCache.get(SEARCH_INDEX_CHANGE).then(lastChange => {\n      lastChange = new Date(lastChange);\n      if (lastChange && lastChange > this._modified) {\n        console.log('Re-index PWAs');\n        // Invalidate index and re-index all PWAs\n        this._initIndex();\n        libPwaIndex.indexAllPwas().then(_ => {\n          const newDate = new Date();\n          this._modified = newDate;\n          this.sarchIndexChange(newDate);\n        });\n      }\n    });\n  }\n}\n\n// Export Search as a singleton object\nmodule.exports = new Search();\n"
  },
  {
    "path": "lib/tasks.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst db = require('../lib/model-datastore');\nconst Task = require('../models/task');\nconst pwaLib = require('../lib/pwa');\nconst tasksLib = require('../lib/tasks');\n\nconst ENTITY_NAME = 'Task';\nconst E_SAVING_TASK = exports.E_SAVING_TASK = 1;\nconst E_GET_TASK_POP = exports.E_GET_TASK_POP = 2;\nconst E_DELETE_TASK_POP = exports.E_DELETE_TASK_POP = 3;\n\n/**\n * Push a Task object into the DB.\n *\n * @param {Task} lighthouse\n * @return {Promise<Task>}\n */\nexports.push = function(task) {\n  return new Promise((resolve, reject) => {\n    db.update(ENTITY_NAME, task.id, task)\n      .then(result => {\n        return resolve(result);\n      })\n      .catch(err => {\n        console.error(err);\n        return reject(E_SAVING_TASK);\n      });\n  });\n};\n\nexports.getTasks = async function(numTasks) {\n  const result =\n      await db.list(ENTITY_NAME, 0, numTasks, {field: 'created', config: {ascending: true}});\n  const tasks = [];\n  for (let entity of result.entities) {\n    tasks.push(Object.assign(new Task(), entity));\n  }\n  return tasks;\n};\n\nexports.deleteTask = async function(taskId) {\n  await db.delete(ENTITY_NAME, taskId);\n};\n\n/**\n * Pop the oldest Task\n *\n * @return {Promise<Task>}\n */\nexports.pop = function() {\n  return new Promise((resolve, reject) => {\n    db.list(ENTITY_NAME, 0, 1, {field: 'created', config: {ascending: true}})\n      .then(result => {\n        if (result.entities.length === 0) {\n          return resolve(null);\n        }\n        let task = Object.assign(new Task(), result.entities[0]);\n        db.delete(ENTITY_NAME, task.id)\n          .then(_ => {\n            return resolve(task);\n          }).catch(err => {\n            console.error('Error deleting task', err);\n            return reject(E_DELETE_TASK_POP);\n          });\n      })\n      .catch(_ => {\n        return reject(E_GET_TASK_POP);\n      });\n  });\n};\n\n/**\n * Execute a task\n *\n * @return {Promise<Task>}\n */\nexports.executePwaTask = function(task) {\n  if (!task) {\n    return Promise.resolve();\n  }\n  return pwaLib.find(task.pwaId)\n    .then(pwa => {\n      // Dynamically get module and function to execute from task with a PWA\n      // const moduleFromTask = require(task.modulePath);\n      const moduleFromTask = require('../lib/pwa');\n      const functionFromTask = Reflect.get(moduleFromTask, task.functionName);\n      return functionFromTask.call(moduleFromTask, pwa)\n        .then(_ => {\n          return task;\n        })\n        .catch(err => {\n          console.error('Error running task: ' + err);\n          task.retries -= 1;\n          if (task.retries >= 0) {\n            tasksLib.push(task);\n          }\n          return task;\n        });\n    })\n    .catch(err => {\n      console.error(err);\n    });\n};\n\n/**\n * Pop the oldest Task and execute it\n *\n * @return {Promise<Task | null>}\n */\nexports.popExecute = function() {\n  return tasksLib.pop()\n    .then(task => {\n      if (task) {\n        return tasksLib.executePwaTask(task);\n      }\n      return null;\n    });\n};\n"
  },
  {
    "path": "lib/verify-id-token.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\nconst CLIENT_ID = config.get('CLIENT_ID');\nconst CLIENT_SECRET = config.get('CLIENT_SECRET');\n\n/**\n * @param {string} idToken\n * @return {Promise<GoogleLogin>}\n */\nexports.verifyIdToken = function(idToken) {\n  const {OAuth2Client} = require('google-auth-library');\n  const client = new OAuth2Client(CLIENT_ID, CLIENT_SECRET);\n  return new Promise((resolve, reject) => {\n    client.verifyIdToken({idToken, CLIENT_ID}, (err, googleLogin) => {\n      if (err) {\n        reject(err);\n      }\n      resolve(googleLogin);\n    });\n  });\n};\n"
  },
  {
    "path": "lib/web-performance.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst config = require('../config/config');\nconst dataFetcher = require('../lib/data-fetcher');\n\nconst WEBPERFORMANCE_SERVER_URL = config.get('WEBPERFORMANCE_SERVER');\nconst WEBPERFORMANCE_SERVER_API_KEY = config.get('WEBPERFORMANCE_SERVER_API_KEY');\nconst WEBPERFORMANCE_SERVER_WEBPAGEURL = WEBPERFORMANCE_SERVER_URL + 'webpageurl';\nconst WEBPERFORMANCE_SERVER_PAGESPEED_REPORT = WEBPERFORMANCE_SERVER_URL + 'pagespeedreport/';\nconst WEBPERFORMANCE_SERVER_WEBPAGETEST_REPORT = WEBPERFORMANCE_SERVER_URL + 'webpagetestreport/';\nconst WEBPERFORMANCE_SERVER_LIGHTHOUSE_REPORT = WEBPERFORMANCE_SERVER_URL + 'lighthousereport/';\n\nfunction submitToWebPerformanceService(pwa) {\n  const body = {\n    id: pwa.id,\n    url: pwa.absoluteStartUrl,\n    source: 'pwa-directory',\n    description: pwa.description,\n    created: pwa.created\n  };\n  return dataFetcher.postJson(\n    WEBPERFORMANCE_SERVER_WEBPAGEURL + '?key=' + WEBPERFORMANCE_SERVER_API_KEY, body);\n}\n\n/**\n * Submit PWA to the WebPerformance service.\n *\n * @param {number} a PWA\n * @return {Promise<Lighthouse>}\n */\nexports.submitWebPageUrl = function(pwa) {\n  return new Promise((resolve, reject) => {\n    submitToWebPerformanceService(pwa)\n      .then(result => {\n        return resolve(result);\n      })\n      .catch(err => {\n        return reject(err);\n      });\n  });\n};\n\n/**\n * Get Report for PWA.\n *\n * @param {PWA} a PWA\n * @return {Promise<Json>}\n */\nfunction getReport(url) {\n  return dataFetcher.fetchWithUA(url)\n    .then(response => {\n      if (response.status === 200) {\n        return response.json();\n      } else if (response.status === 404) {\n        return Promise.reject('not available yet');\n      }\n      return Promise.reject(response);\n    })\n    .catch(err => {\n      return Promise.reject(err);\n    });\n}\n\n/**\n * Get PageSpeed Report for PWA.\n *\n * @param {PWA} a PWA\n * @return {Promise<Json>}\n */\nexports.getPageSpeedReport = function(pwa) {\n  return getReport(WEBPERFORMANCE_SERVER_PAGESPEED_REPORT + pwa.id + '?limit=1');\n};\n\n/**\n * Get WebPageTest Report for PWA.\n *\n * @param {PWA} a PWA\n * @return {Promise<Json>}\n */\nexports.getWebPageTestReport = function(pwa) {\n  return getReport(WEBPERFORMANCE_SERVER_WEBPAGETEST_REPORT + pwa.id + '?limit=1');\n};\n\n/**\n * Get Lighthouse Report for PWA.\n *\n * @param {PWA} a PWA\n * @return {Promise<Json>}\n */\nexports.getLighthouseReport = function(pwa) {\n  return getReport(WEBPERFORMANCE_SERVER_LIGHTHOUSE_REPORT + pwa.id + '?limit=1');\n};\n"
  },
  {
    "path": "lighthouse_machine/.dockerignore",
    "content": ".gitignore\n.git\noutputs"
  },
  {
    "path": "lighthouse_machine/.eslintrc.json",
    "content": "{\n  \"extends\": \"google\",\n  \"installedESLint\": true,\n  // http://eslint.org/docs/rules/\n  \"rules\": {\n    \"max-len\": [2, 100, {\n      \"ignoreComments\": true,\n      \"ignoreUrls\": true,\n      \"tabWidth\": 2\n    }],\n    \"no-implicit-coercion\": [2, {\n      \"boolean\": false,\n      \"number\": true,\n      \"string\": true\n    }],\n    \"no-unused-expressions\": [2, {\n      \"allowShortCircuit\": true,\n      \"allowTernary\": false\n    }],\n    \"no-unused-vars\": [2, {\n      \"vars\": \"all\",\n      \"args\": \"after-used\",\n      \"argsIgnorePattern\": \"(^reject$|^_$)\",\n      \"varsIgnorePattern\": \"(^_$)\"\n    }],\n    \"quotes\": [2, \"single\"],\n    \"require-jsdoc\": 0,\n    \"valid-jsdoc\": 0,\n    \"prefer-arrow-callback\": 1,\n    \"no-var\": 1\n  },\n  // http://eslint.org/docs/user-guide/configuring#specifying-environments\n  \"env\": {\n    \"node\": true\n  }\n}\n"
  },
  {
    "path": "lighthouse_machine/.gitignore",
    "content": "outputs\nnode_modules\n"
  },
  {
    "path": "lighthouse_machine/Dockerfile",
    "content": "#\tCopyright 2016-2017, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nFROM ubuntu:latest\n\n## PART 1: Core components\n## =======================\n\n# Install utilities\nRUN apt-get update --fix-missing && apt-get -y upgrade &&\\\napt-get install -y sudo apt-utils curl wget unzip git gnupg\n\n# Install node 10\nRUN curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash - &&\\\nsudo apt-get install -y nodejs\n\n# Install Xvfb and dbus for X11\nRUN apt-get install -y xvfb dbus-x11\n\n# Install Chrome for Ubuntu\nRUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add - &&\\\nsudo sh -c 'echo \"deb http://dl.google.com/linux/chrome/deb/ stable main\" >> /etc/apt/sources.list.d/google-chrome.list' &&\\\nsudo apt-get update &&\\\nsudo apt-get install -y google-chrome-stable\n\n# Install Yarn\nRUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - &&\\\necho \"deb https://dl.yarnpkg.com/debian/ stable main\" | sudo tee /etc/apt/sources.list.d/yarn.list &&\\\nsudo apt-get update && sudo apt-get install yarn\n\n# Copy key documents (except .dockerignored files)\nCOPY etc/xvfb /etc/init.d/xvfb\nRUN chmod +x /etc/init.d/xvfb\n\n# Add a user and make it a sudo user\nRUN useradd -m chromeuser\n\n# Copy the chrome-user script used to start Chrome as non-root\nCOPY chromeuser-script.sh /\nRUN chmod +x /chromeuser-script.sh\n\n## PART 2: Lighthouse\n## ==================\n\n# Download lighthouse\nRUN git clone https://github.com/googlechrome/lighthouse &&\\\ncd /lighthouse &&\\\ngit checkout tags/v4.2.0 &&\\\nnpm install -g yarn &&\\\nnpm install -g yarnpkg &&\\\nnpm install -g @types/mkdirp &&\\\nnpm install -g --save-dev run-sequence &&\\\nnpm install -g typescript &&\\\nnpm install -g &&\\\nyarn global add lighthouse\n\n## PART 3: Express server\n## ======================\n\n# Install express\nCOPY package.json /\nRUN npm install\n\n# Add the simple server file\nCOPY server.js /\nRUN chmod +x /server.js\n\n# Add the cpu monitor file\nCOPY cpu_monitor.js /\nRUN chmod +x /cpu_monitor.js\n\n# Generate a self-signed SSL certificate\nRUN openssl req \\\n  -new \\\n  -newkey rsa:4096 \\\n  -days 365 \\\n  -nodes \\\n  -x509 \\\n  -subj \"/C=GB/ST=None/L=None/O=Google/CN=lighthouse-machine-X\" \\\n  -keyout key.pem \\\n  -out cert.pem\n\n# Expose ports 8080 and 8443\nEXPOSE 8080\nEXPOSE 8443\n\n## PART 4: Final setup\n## ===================\n\n# Set the entrypoint\nCOPY entrypoint.sh /\nRUN chmod +x /entrypoint.sh\nENTRYPOINT [\"/entrypoint.sh\"]\n"
  },
  {
    "path": "lighthouse_machine/LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the\n      purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communiampion sent\n      to the Licensor or its representatives, including but not limited to\n      communiampion on electronic mailing lists, source code control\n      systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communiampion that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2015, Google Inc.\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "lighthouse_machine/README.md",
    "content": "# Lighthouse machine\nA Docker image to run [Lighthouse](https://github.com/GoogleChrome/lighthouse) scores on a server\n\n## Build the image\n```bash\ndocker build --no-cache -t lighthouse_machine .\n```\n\n## Run the container\n```bash\n# Run a new container\ndocker run -d -p 8080:8080 --cap-add=SYS_ADMIN lighthouse_machine\n```\n\n## Usage\n```bash\ncurl -X GET 'http://localhost:8080?format=${format}&url=${url}'\n```\n\nwhere `format`is one of `json`, `html` (see [cli-options](https://github.com/GoogleChrome/lighthouse#cli-options) for more information)\n\n## License\nSee [LICENSE](./LICENSE) for more.\n\n## Disclaimer\nThis is not a Google product.\n"
  },
  {
    "path": "lighthouse_machine/app.yaml",
    "content": "#\tCopyright 2016-2017, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nruntime: custom\nenv: flex\nservice: lighthouse-machine\nautomatic_scaling:\n  min_num_instances: 2\n  max_num_instances: 6\n  cool_down_period_sec: 60\n  cpu_utilization:\n    target_utilization: 0.6\n\nresources:\n  cpu: 1\n  memory_gb: 4\n  disk_size_gb: 10\n\nhandlers:\n- url: /.*\n  script: IGNORED\n  secure: always\n\nliveness_check:\n   path: '/_ah/health'\n   check_interval_sec: 30\n   timeout_sec: 4\n   failure_threshold: 3\n   success_threshold: 2\n   initial_delay_sec: 60\n\nreadiness_check:\n  path: '/_ah/busy'\n  check_interval_sec: 3\n  timeout_sec: 2\n  failure_threshold: 1\n  success_threshold: 1\n  app_start_timeout_sec: 300\n\nnetwork:\n  instance_tag: lighthouse-machine\n"
  },
  {
    "path": "lighthouse_machine/chromeuser-script.sh",
    "content": "#!/bin/bash\n\n# Copyright 2016-2017, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nsudo chown -R chromeuser:chromeuser $TMP_PROFILE_DIR\nexport DISPLAY=:0\nXvfb :0 -screen 0 1024x768x24 &\nnohup google-chrome --no-first-run --disable-gpu --no-sandbox --user-data-dir=$TMP_PROFILE_DIR --remote-debugging-port=9222 'about:blank' &\n"
  },
  {
    "path": "lighthouse_machine/cpu_monitor.js",
    "content": "/**\n * Copyright 2016-2017, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst os = require('os');\n\n// Create function to get CPU information\nfunction cpuAverage() {\n  // Initialise sum of idle and time of cores and fetch CPU info\n  let totalIdle = 0;\n  let totalTick = 0;\n  let cpus = os.cpus();\n\n  // Loop through CPU cores\n  for (let i = 0, len = cpus.length; i < len; i++) {\n    // Select CPU core\n    let cpu = cpus[i];\n\n    // Total up the time in the cores tick\n    // eslint-disable-next-line guard-for-in\n    for (let type in cpu.times) {\n      totalTick += cpu.times[type];\n    }\n\n    // Total up the idle time of the core\n    totalIdle += cpu.times.idle;\n  }\n\n  // Return the average Idle and Tick times\n  return {idle: totalIdle / cpus.length, total: totalTick / cpus.length};\n}\n\nmodule.exports = (avgTime, callback) => {\n  this.samples = [];\n  this.samples[1] = cpuAverage();\n  this.refresh = setInterval(() => {\n    this.samples[0] = this.samples[1];\n    this.samples[1] = cpuAverage();\n    let totalDiff = this.samples[1].total - this.samples[0].total;\n    let idleDiff = this.samples[1].idle - this.samples[0].idle;\n    callback(1 - idleDiff / totalDiff);\n  }, avgTime);\n};\n"
  },
  {
    "path": "lighthouse_machine/entrypoint.sh",
    "content": "#!/bin/bash\n\n# Copyright 2016-2017, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n/etc/init.d/dbus start\n/etc/init.d/xvfb start\nsleep 1s\n\nexport DISPLAY=:1\nTMP_PROFILE_DIR=$(mktemp -d -t lighthouse.XXXXXXXXXX)\n\nsu chromeuser\nsource /chromeuser-script.sh\nsleep 3s\n\nnode /server.js\n"
  },
  {
    "path": "lighthouse_machine/etc/xvfb",
    "content": "#!/bin/bash\n\n# Copyright 2016-2017, Google, Inc.\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nXVFB_OUTPUT=/tmp/Xvfb.out\nXVFB=/usr/bin/X11/Xvfb\nXVFB_OPTIONS=\":1 -screen 0 1024x768x24 -fbdir /var/run\"\n\nstart()  {\necho -n \"Starting : X Virtual Frame Buffer \"\n$XVFB $XVFB_OPTIONS >>$XVFB_OUTPUT 2>&1&\nRETVAL=$?\necho\nreturn $RETVAL\n}\n\nstop()   {\necho -n \"Shutting down : X Virtual Frame Buffer\"\necho\npkill Xvfb\necho\nreturn 0\n}\n\ncase \"$1\" in\nstart)\nstart\n;;\nstop)\nstop\n;;\nstatus)\nstatus xvfb\n;;\nrestart)\n    stop\n    start\n    ;;\n\n*)\necho \"Usage: xvfb {start|stop|status|restart}\"\nexit 1\n;;\nesac\nexit $?\n"
  },
  {
    "path": "lighthouse_machine/package.json",
    "content": "{\n  \"name\": \"lighthouse_machine_server\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A server for the lighthouse machine\",\n  \"repository\": \"https://github.com/GoogleChrome/gulliver/lighthouse_machine\",\n  \"author\": \"Google Inc.\",\n  \"contributors\": [\n    {\n      \"name\": \"Cedric Bellet\",\n      \"email\": \"cbellet@google.com\"\n    },\n    {\n      \"name\": \"Julian Toledo\",\n      \"email\": \"jtoledo@google.com\"\n    }\n  ],\n  \"license\": \"Apache-2.0\",\n  \"main\": \"server.js\",\n  \"scripts\": {\n    \"start\": \"node server.js\"\n  },\n  \"dependencies\": {\n    \"express\": \"^4.16.3\"\n  }\n}\n"
  },
  {
    "path": "lighthouse_machine/server.js",
    "content": "/**\n * Copyright 2016-2017, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst express = require('express');\nconst exec = require('child_process').exec;\nconst http = require('http');\nconst https = require('https');\nconst fs = require('fs');\nconst cpuMonitor = require('./cpu_monitor');\n\n// Chrome panick\nlet chromePanick = false;\n\n// CPU monitoring\nlet cpuPoints = new Array(5);\nlet cpuAlert = false;\n\ncpuMonitor(60000, load => {\n  // Add new measurements to the cpuPoints array\n  cpuPoints.pop();\n  cpuPoints.unshift(load);\n\n  // Calculate the avg and spread of the cpuPoints array\n  let sum = 0;\n  let i = 5;\n  while (i--) sum += cpuPoints[i];\n  let avg = sum / 5;\n  let spread = Math.max.apply(Math, cpuPoints) - Math.min.apply(Math, cpuPoints);\n\n  // If the CPU load is above 80% and the spread is less than 10%, trigger an alert\n  cpuAlert = (avg > 0.8 && spread < 0.1);\n  cpuAlert && console.log(`Average: ${avg}, Spread: ${spread}`);\n});\n\n// Constants\nconst HTTP_PORT = 8080;\nconst HTTPS_PORT = 8443;\n\n// HTTPS options\nconst options = {\n  key: fs.readFileSync('key.pem'),\n  cert: fs.readFileSync('cert.pem')\n};\n\n// App\nconst app = express();\nlet isBusy = false;\n\n// Main endpoint\napp.get('/', (req, res) => {\n  if (isBusy) {\n    res.sendStatus(429);\n  } else {\n    isBusy = true;\n    res.setTimeout(500000, _ => {\n      console.log('Request has timed out.');\n      res.send(408);\n    });\n    try {\n      exec(\n        `lighthouse '${req.query.url}' --port 9222 --output-path ../report.${req.query.format} --output ${req.query.format}`,\n        {\n          cwd: '/lighthouse',\n          timeout: 500000\n        },\n        error => {\n          if (error !== null) {\n            console.log(`exec error: ${error}`);\n\n            // This is for when Chrome crashes and Lighthouse is unable to reconnect\n            // to an appropriate instance of Chrome\n            if (error.message.includes('Unable to connect')) {\n              chromePanick = true;\n            }\n          }\n\n          isBusy = false;\n          res.sendFile(`/report.${req.query.format}`);\n        }\n      );\n    } catch (e) {\n      isBusy = false;\n      res.status(500).send(e);\n    }\n  }\n});\n\n// Auto-healing endpoint\napp.get('/_ah/health', (req, res) => {\n  if (chromePanick) {\n    // If we have a Chrome panick send a 500\n    res.sendStatus(500);\n  } else if (cpuAlert) {\n    // if we have a CPU alert send a 500, otherwise send a 200\n    res.sendStatus(500);\n  } else {\n    res.sendStatus(200);\n  }\n});\n\n// Busy-ness endpoit\napp.get('/_ah/busy', (req, res) => {\n  if (isBusy) {\n    res.sendStatus(503);\n  } else {\n    res.sendStatus(200);\n  }\n});\n\nhttp.createServer(app).listen(HTTP_PORT);\nhttps.createServer(options, app).listen(HTTPS_PORT);\n\nconsole.log(\n  `Running on https://localhost:${HTTPS_PORT} and http://localhost:${HTTP_PORT}`\n);\n"
  },
  {
    "path": "middlewares/index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nconst express = require('express');\nconst asset = require('../lib/asset-hashing').asset;\nconst router = express.Router(); // eslint-disable-line new-cap\nconst CSSPATH = asset.encode('/css/style.css');\nconst JSPATH = asset.encode('/js/gulliver.js');\n\nrouter.use((req, res, next) => {\n  res.setHeader('Content-Type', 'text/html');\n\n  /* eslint-disable quotes */\n  res.setHeader('content-security-policy', [\n    `connect-src 'self' https://www.google-analytics.com https://web-performance-dot-pwa-directory.appspot.com https://fcm.googleapis.com`,\n    `default-src 'self' https://accounts.google.com https://apis.google.com https://fcm.googleapis.com`,\n    `script-src 'self' 'unsafe-eval' https://apis.google.com https://www.google-analytics.com https://www.gstatic.com`,\n    `style-src 'self' 'unsafe-inline' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/ https://www.gstatic.com`,\n    `font-src 'self' https://cdnjs.cloudflare.com/ajax/libs/font-awesome/`,\n    `img-src 'self' https://storage.googleapis.com https://www.google-analytics.com`\n  ].join('; '));\n  /* eslint-enable quotes */\n  res.setHeader('x-content-type-options', 'nosniff');\n  res.setHeader('x-dns-prefetch-control', 'off');\n  res.setHeader('x-download-options', 'noopen');\n  res.setHeader('x-frame-options', 'SAMEORIGIN');\n  res.setHeader('x-xss-protection', '1; mode=block');\n\n  // Set the preload header if a full render is being requested.\n  if (!req.query.contentOnly && !req.originalUrl.startsWith('/.app/')) {\n    res.setHeader('Link',\n      `<${CSSPATH}>; rel=preload; as=style, <${JSPATH}>; rel=preload; as=script`);\n  }\n  next();\n});\n\nmodule.exports = router;\n"
  },
  {
    "path": "models/favorite-pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\n/**\n * Favorite Pwa for a user\n */\nclass FavoritePwa {\n  constructor(pwaId, userId) {\n    this.id = pwaId + '-' + userId;\n    this.pwaId = pwaId;\n    this.userId = userId;\n  }\n}\n\nmodule.exports = FavoritePwa;\n"
  },
  {
    "path": "models/lighthouse.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the 'License');\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an 'AS IS' BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst libLighthouse = require('../lib/lighthouse');\n\n/**\n * Class representing a Lighthouse report for a PWA\n *\n * absoluteStartUrl is the absoluteStartUrl of the PWA\n * lighthouseJson is the Lighthouse's report as JSON object\n */\nclass Lighthouse {\n  constructor(pwaId, absoluteStartUrl, lighthouseJson) {\n    this.pwaId = pwaId;\n    this.absoluteStartUrl = absoluteStartUrl;\n    this._lighthouseJson = parseToJson(lighthouseJson);\n    this.lighthouseInfo = libLighthouse.processLighthouseJson(this._lighthouseJson);\n    this.totalScore = this.lighthouseInfo.totalScore;\n    this.lighthouseVersion = this.lighthouseInfo.lighthouseVersion;\n    this.date = (new Date()).toISOString().slice(0, 10);\n    this.id = this.pwaId + '-' + this.date;\n  }\n\n  get lighthouseJson() {\n    return this._lighthouseJson;\n  }\n\n  set lighthouseJson(value) {\n    // lighthouseJson is stored as a string in the datastore\n    this._lighthouseJson = parseToJson(value);\n  }\n}\n\nfunction parseToJson(value) {\n  if (value && Object.prototype.toString.call(value) === '[object String]') {\n    return JSON.parse(value);\n  }\n  return value;\n}\n\nmodule.exports = Lighthouse;\n"
  },
  {
    "path": "models/manifest.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\nconst url = require('url');\n\n/**\n * Class representing a Web App Manifest\n */\nclass Manifest {\n  constructor(manifestUrl, jsonManifest) {\n    this.url = manifestUrl;\n    this.raw = JSON.stringify(jsonManifest);\n    this.name = jsonManifest.name;\n    this.shortName = jsonManifest.short_name;\n    this.description = jsonManifest.description;\n    this.startUrl = jsonManifest.start_url;\n    this.backgroundColor = jsonManifest.background_color;\n    this.icons = jsonManifest.icons;\n    this.scope = jsonManifest.scope;\n  }\n\n  getBestIcon() {\n    function getIconSize(icon) {\n      if (!icon.sizes) {\n        return 0;\n      }\n      return parseInt(icon.sizes.substring(0, icon.sizes.indexOf('x')), 10);\n    }\n\n    if (!this.icons) {\n      return null;\n    }\n\n    let bestIcon;\n    let bestIconSize;\n\n    for (let icon of this.icons) {\n      if (!bestIcon) {\n        bestIcon = icon;\n        bestIconSize = getIconSize(icon);\n      }\n\n      const iconSize = getIconSize(icon);\n      if (iconSize > bestIconSize) {\n        bestIcon = icon;\n        bestIconSize = iconSize;\n      }\n\n      // We can return 128 and 144 even if there are bigger ones.\n      if (iconSize === 128 || iconSize === 144) {\n        return icon;\n      }\n    }\n    return bestIcon;\n  }\n\n  /** Gets the Url for the largest icon in the Manifest */\n  getBestIconUrl() {\n    let bestIcon = this.getBestIcon();\n    if (!bestIcon || !bestIcon.src) {\n      return '';\n    }\n    return url.resolve(this.url, bestIcon.src);\n  }\n}\n\nmodule.exports = Manifest;\n"
  },
  {
    "path": "models/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst uri = require('urijs');\nconst URL = require('url');\nconst Manifest = require('../models/manifest');\nconst User = require('../models/user');\n\nclass Pwa {\n  constructor(manifestUrl, manifestModel) {\n    // remove hash from url\n    manifestUrl && (this.manifestUrl = removeHash(manifestUrl));\n    this._manifest = stringifyManifestIfNeeded(manifestModel);\n    this.created = new Date();\n    this.updated = this.created;\n    this.visible = true;\n  }\n\n  get shortName() {\n    if (!this.manifest) {\n      return '';\n    }\n    return this.manifest.shortName || '';\n  }\n\n  get name() {\n    if (!this.manifest) {\n      return '';\n    }\n    return this.manifest.name || '';\n  }\n\n  get displayName() {\n    return this.name ||\n      this.shortName ||\n      trimManifestFile(this.manifestUrl);\n  }\n\n  get description() {\n    if (this.manifest && this.manifest.description) {\n      return this.manifest.description;\n    }\n\n    return this.metaDescription || '';\n  }\n\n  get startUrl() {\n    if (!this.manifest) {\n      return '';\n    }\n    return this.manifest.startUrl || '';\n  }\n\n  get absoluteStartUrl() {\n    if (!this.manifestUrl) {\n      return '';\n    }\n\n    const startUrl = this.startUrl || '/';\n    return this._cleanUrl(uri(startUrl).absoluteTo(this.manifestUrl).toString());\n  }\n\n  get backgroundColor() {\n    if (!this.manifest) {\n      return '#ffffff';\n    }\n\n    return this.manifest.backgroundColor || '#ffffff';\n  }\n\n  get manifest() {\n    if (!this._manifest) {\n      return null;\n    }\n    return new Manifest(this.manifestUrl, JSON.parse(this._manifest));\n  }\n\n  set manifest(value) {\n    if (value && typeof value === 'object') {\n      this._manifest = value.raw;\n    } else {\n      this._manifest = value;\n    }\n  }\n\n  get manifestAsString() {\n    return this._manifest;\n  }\n\n  setUser(user) {\n    this.user = new User(user);\n  }\n\n  generateEncodedStartUrl() {\n    const parsedUrl = URL.parse(this.absoluteStartUrl);\n    this.encodedStartUrl = encodeURIComponent(parsedUrl.hostname + parsedUrl.pathname);\n    return this.encodedStartUrl;\n  }\n\n  isNew() {\n    return this.created === this.updated;\n  }\n\n  _cleanUrl(input) {\n    const url = new URL.URL(input);\n    for (const name of url.searchParams.keys()) {\n      if (name.toLowerCase().startsWith('utm_')) {\n        url.searchParams.delete(name);\n      }\n    }\n    return url.toString();\n  }\n}\n\nfunction trimManifestFile(url) {\n  let startIndex = url.indexOf('//');\n  if (startIndex === -1) {\n    startIndex = 0;\n  } else {\n    startIndex += 2;\n  }\n  let endIndex = url.lastIndexOf('/');\n  if (endIndex === -1) {\n    endIndex = url.length;\n  }\n  return url.substring(startIndex, endIndex);\n}\n\nfunction stringifyManifestIfNeeded(manifest) {\n  if (manifest && typeof manifest === 'object') {\n    return manifest.raw;\n  }\n  return manifest;\n}\n\nfunction removeHash(urlString) {\n  const url = URL.parse(urlString);\n  url.hash = '';\n  return url.format();\n}\n\nmodule.exports = Pwa;\n"
  },
  {
    "path": "models/task.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nclass Task {\n  constructor(pwaId, modulePath, functionName, retries) {\n    this.pwaId = pwaId;\n    this.modulePath = modulePath;\n    this.functionName = functionName;\n    this.retries = retries;\n    this.created = new Date();\n  }\n}\n\nmodule.exports = Task;\n"
  },
  {
    "path": "models/user.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\n\nconst crypto = require('crypto');\n\n/**\n * User from google-auth-library-nodejs client\n */\nclass User {\n  constructor(googleLogin) {\n    this.id = crypto.createHash('sha1').update(googleLogin.getPayload().sub).digest('hex');\n  }\n}\n\nmodule.exports = User;\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"gulliver\",\n  \"version\": \"1.0.0\",\n  \"description\": \"A directory of PWAs\",\n  \"repository\": \"https://github.com/GoogleChrome/gulliver\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"node app.js\",\n    \"prestart\": \"BABEL_ENV=default rollup -c rollup-config/gulliver.js && npm run generate-msg-sw\",\n    \"monitor\": \"nodemon app.js\",\n    \"deploy\": \"npm run prestart && gcloud app deploy app.yaml\",\n    \"mocha-app\": \"_mocha test/app/**/* --exit\",\n    \"mocha-client\": \"BABEL_ENV=test _mocha --compilers js:babel-core/register test/client/**/*.js\",\n    \"coverage\": \"istanbul cover _mocha --compilers js:babel-core/register test/app/**/*\",\n    \"lint\": \"eslint .\",\n    \"test\": \"npm run lint && npm run mocha-client && npm run mocha-app\",\n    \"generate-msg-sw\": \"node firebase-messaging-sw-generator.js\",\n    \"lint-fix\": \"eslint --fix .\"\n  },\n  \"author\": \"Google Inc.\",\n  \"contributors\": [\n    {\n      \"name\": \"Julian Toledo\",\n      \"email\": \"jtoledo@google.com\"\n    },\n    {\n      \"name\": \"Michael Stillwell\",\n      \"email\": \"stillers@google.com\"\n    },\n    {\n      \"name\": \"Andre Bandarra\",\n      \"email\": \"andreban@google.com\"\n    }\n  ],\n  \"license\": \"Apache Version 2.0\",\n  \"semistandard\": {\n    \"globals\": [\n      \"after\",\n      \"afterEach\",\n      \"before\",\n      \"beforeEach\",\n      \"describe\",\n      \"it\"\n    ]\n  },\n  \"engines\": {\n    \"nodejs8\": \"8.12.0\"\n  },\n  \"dependencies\": {\n    \"@google-cloud/datastore\": \"^1.4.2\",\n    \"@google-cloud/storage\": \"^1.7.0\",\n    \"babel-preset-es2015-rollup\": \"^3.0.0\",\n    \"body-parser\": \"^1.18.3\",\n    \"cheerio\": \"^0.22.0\",\n    \"compression\": \"^1.7.3\",\n    \"elasticlunr\": \"^0.9.5\",\n    \"escape-html\": \"^1.0.3\",\n    \"express\": \"^4.16.4\",\n    \"express-csv\": \"^0.6.0\",\n    \"express-minify-html\": \"^0.12.0\",\n    \"express-sslify\": \"^1.2.0\",\n    \"firebase\": \"^5.5.9\",\n    \"google-auth-library\": \"^1.6.1\",\n    \"handlebars\": \"^4.5.3\",\n    \"hbs\": \"^4.0.4\",\n    \"http-parser-js\": \"^0.4.13\",\n    \"jsdom\": \"^9.5.0\",\n    \"lodash.merge\": \"^4.6.2\",\n    \"lodash.template\": \"^4.5.0\",\n    \"memcached\": \"^2.2.2\",\n    \"mime-types\": \"^2.1.21\",\n    \"moment\": \"^2.22.2\",\n    \"multer\": \"^1.4.1\",\n    \"nconf\": \"^0.8.4\",\n    \"node-fetch\": \"^2.6.1\",\n    \"parse-color\": \"^1.0.0\",\n    \"request\": \"^2.88.0\",\n    \"rev-hash\": \"^1.0.0\",\n    \"rollup\": \"^0.58.2\",\n    \"rollup-plugin-babel\": \"^2.6.1\",\n    \"rollup-plugin-commonjs\": \"^9.2.0\",\n    \"rollup-plugin-node-resolve\": \"^2.0.0\",\n    \"rollup-plugin-uglify\": \"^1.0.1\",\n    \"rss\": \"^1.2.2\",\n    \"serve-static\": \"^1.11.1\",\n    \"sharp\": \"^0.17.0\",\n    \"spdy\": \"^3.4.7\",\n    \"strong-data-uri\": \"^1.0.6\",\n    \"sw-offline-google-analytics\": \"^1.1.1\",\n    \"sw-toolbox\": \"^3.2.1\",\n    \"urijs\": \"^1.18.1\",\n    \"url-polyfill\": \"^1.1.0\",\n    \"whatwg-fetch\": \"^2.0.1\",\n    \"yaku\": \"^0.17.6\"\n  },\n  \"devDependencies\": {\n    \"babel-preset-es2015\": \"^6.24.1\",\n    \"chai\": \"^3.0.0\",\n    \"chai-as-promised\": \"^6.0.0\",\n    \"eslint\": \"^6.6.0\",\n    \"eslint-config-google\": \"^0.6.0\",\n    \"istanbul\": \"^0.4.4\",\n    \"mocha\": \"^5.2.0\",\n    \"node-mocks-http\": \"^1.7.3\",\n    \"simple-mock\": \"^0.7.0\",\n    \"supertest\": \"^3.3.0\"\n  }\n}\n"
  },
  {
    "path": "public/.well-known/assetlinks.json",
    "content": "[{\n    \"relation\": [\"delegate_permission/common.handle_all_urls\"],\n    \"target\": {\n      \"namespace\": \"android_app\",\n      \"package_name\": \"com.appspot.pwa_directory\",\n      \"sha256_cert_fingerprints\": [\"1A:64:23:29:C2:BB:FA:18:45:A3:BE:02:08:DD:B4:8F:51:21:F9:2E:95:75:75:CA:2B:8B:47:75:94:C5:0F:64\"]}\n  }]\n"
  },
  {
    "path": "public/css/style.css",
    "content": "/* Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. */\n\nhtml {\n  height: 100%;\n  box-sizing: border-box;\n  overflow-x: hidden;\n}\n*,\n*:before,\n*:after {\n  box-sizing: inherit;\n  /* don't show Chrome's default blue tap highlight */\n  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n\nbody {\n  font-size: 16px;\n  font-weight: 300;\n  font-family:-apple-system,BlinkMacSystemFont,\"Segoe UI\",Roboto,Oxygen-Sans,Ubuntu,Cantarell,\"Helvetica Neue\",sans-serif;\n  -moz-osx-font-smoothing: grayscale;\n  -webkit-font-smoothing: antialiased;\n  width: 100%;\n  min-width: 310px;\n  margin: 0;\n  background: #F5F5F5;\n  position: relative;\n  min-height: 100%;\n  padding-bottom: 10px;\n  overflow-x: hidden;\n}\n\npre, code {\n  font-family: Consolas,Menlo,Monaco,Lucida Console,Liberation Mono,DejaVu Sans Mono,Bitstream Vera Sans Mono,Courier New,monospace,sans-serif;\n  font-size: 14px;\n}\n\nh3 {\n  font-size: 22px;\n  font-weight: 400;\n  letter-spacing: -.018em;\n  text-overflow: ellipsis;\n}\n\na {\n  text-decoration: none;\n  color: inherit;\n}\na:hover {\n  text-decoration: underline;\n}\nmain {\n  padding-top: 16px;\n  padding-right: 8px;\n  padding-left: 8px;\n  padding-bottom: 44px;\n  transition: opacity 0.3s ease-in-out;\n  width: 100vw;\n}\n\n/* Navbar */\n.navbar {\n  position: relative;\n  height: 82px;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n}\n.navbar-title {\n  height: 55px;\n  color: white;\n  font-size: 30px;\n  text-align: center;\n  line-height: 55px;\n}\n.navbar-title #title {\n  -ms-flex-item-align: start;\n      align-self: flex-start;\n  margin: 0 auto;\n  text-align: left;\n  padding-left: 55px;\n  background-position: left center;\n  background-repeat: no-repeat;\n  background-image: url(/favicons/android-chrome-48x48.png);\n  background-image: -webkit-image-set( url(/favicons/android-chrome-48x48.png) 1x, url(/favicons/android-chrome-96x96.png) 2x );\n}\n.navbar-subtitle {\n  height: 20px;\n  color: white;\n  font-size: 18px;\n  text-align: center;\n  line-height: 20px;\n}\n.navbar a {\n  color: white;\n  text-decoration: none;\n}\n.section {\n  color: white;\n  font-size: 16px;\n  font-weight: 500;\n}\n.section-top {\n  height: 48px;\n  line-height: 48px;\n  display: flex;\n  display: -webkit-flex;\n}\n.section-tabs {\n  height: 48px;\n  line-height: 48px;\n  display: flex;\n  display: -webkit-flex;\n}\n.section-top #subtitle {\n  margin: 0 16px;\n}\n.back {\n  height: 48px;\n  float: left;\n}\n.back svg {\n  fill: white;\n}\n.back:hover {\n  opacity: 0.5;\n  cursor: pointer;\n}\n.back:active {\n  background-color: #90CAF9;\n}\n.section-tabs .tab:active,\n.section-tabs .back:active {\n  background-color: #90CAF9;\n}\n.section-tabs .tab {\n  height: 48px;\n  line-height: 48px;\n  margin: 0;\n  opacity: 0.7;\n  font-weight: 500;\n  text-transform: uppercase;\n  flex-grow: 1;\n  text-align: center;\n}\n.section-tabs .activetab {\n  color: white;\n  border-bottom-style: solid;\n  opacity: 1;\n}\n.section-tabs a, .section-tabs a:visited {\n  color: white;\n  text-decoration: none;\n}\n.section-tabs.tab:hover {\n  border-bottom-style: solid;\n  background: #90CAF9;\n  border-color: #FF8A65;\n  border-width: 2dp;\n  opacity: 1;\n}\n\n@media (min-width: 904px) {\n  .section {\n    height: 48px;\n  }\n  .section-top {\n    max-width: 50%;\n    min-width: 50%;\n    float: right;\n  }\n  .section-tabs {\n    position: relative;\n    max-width: 50%;\n  }\n}\n\n/* Flex NavBar */\n@media (max-width: 607px) {\n  .navbar {\n    height: 55px;\n    padding-left: 8px;\n  }\n  .navbar-title {\n    font-size: 24px;\n    text-align: left;\n  }\n  .navbar-title #title {\n    padding-left: 40px;\n    background-image: url(/favicons/android-chrome-36x36.png);\n    background-image: -webkit-image-set( url(/favicons/android-chrome-36x36.png) 1x, url(/favicons/android-chrome-72x72.png) 2x );\n  }\n  .navbar-subtitle {\n    height: 0px;\n  }\n  .navbar .notifications {\n    bottom: 16px;\n  }\n  .hide-on-mobile {\n    display: none;\n  }\n}\n.button-primary {\n  border: 1px solid transparent;\n  border-radius: 2px;\n  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\n  max-width: 100px;\n}\n.button {\n  background: none;\n  font-size: 16px;\n  font-weight: 500;\n  color: white;\n  min-width: 120px;\n  height: 30px;\n  text-align: center;\n  display: inline-block;\n  -ms-touch-action: manipulation;\n  touch-action: manipulation;\n  cursor: pointer;\n  -webkit-user-select: none;\n  -moz-user-select: none;\n  -ms-user-select: none;\n  user-select: none;\n  background-image: none;\n  border: none;\n  text-transform: uppercase;\n  margin: 0;\n  transition: opacity 0.2s ease-in-out;\n}\n\nbutton:active,\nbutton[disabled=disabled],\nbutton:disabled {\n  opacity: 0.5;\n  cursor: default;\n}\n.button:not([disabled]):hover {\n  opacity: 0.5;\n}\n.button a {\n  color: white;\n  text-decoration: none;\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n\n.box-shadow {\n  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\n}\n\n/* PWA List Box */\n.card-pwa {\n  text-overflow: hidden;\n  border-radius: 2px;\n  padding: 8px;\n  display: flex;\n  align-items: center;\n  height: 132px;\n  flex-direction: column;\n  transition: opacity .2s ease-in-out;\n  pointer-events: auto;\n}\n\na.card-pwa:hover {\n  text-decoration: none;\n}\n\n.card-pwa .pwa-name {\n  padding-top: 8px;\n  font-size: 18px;\n  font-weight: 600;\n  height: 36px;\n  text-align: center;\n  letter-spacing: .005em;\n  overflow: hidden;\n  text-overflow: ellipsis;\n  width: 100%;\n  white-space: nowrap;\n}\n\n.pwa-icon {\n  width: 64px;\n  height: 64px;\n  text-align: center;\n  font-size: 64px;\n}\n\n.score {\n  float: right;\n  font-weight: 600;\n  background: no-repeat center left url(/img/lighthouse-18.png);\n  background: no-repeat center left -webkit-image-set(\n    url(/img/lighthouse-18.png) 1x,\n    url(/img/lighthouse-36.png) 2x\n  );\n  padding-left: 24px;\n}\n\n.detail-general {\n  max-width: 600px;\n  -webkit-display: flex;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-flow: column;\n      flex-flow: column;\n  -webkit-box-pack: justify;\n      -ms-flex-pack: justify;\n  justify-content: space-between;\n  margin: 0 auto 16px auto;\n  padding: 0;\n  text-overflow: hidden;\n  font-weight: 400;\n  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\n  background: white;\n  border-radius: 2px;\n}\n.detail-general h3 {\n  color: #333333;\n  margin: 0;\n  padding: 16px;\n}\n.detail-general a {\n  color: #1976D2;\n  text-decoration: underline;\n}\n.transition .detail-general {\n  opacity: 0;\n}\n\n/* Lighthouse details */\n.lighthouse-details {\n  padding-bottom: 0.5em;\n  text-align: center;\n  font-size: 18px;\n}\n.viewer-placeholder-logo {\n  vertical-align:middle;\n  padding-right: 8px;\n}\n\n/* PWA Overview */\n#pwa {\n  padding: 16px;\n  display: flex;\n  flex-direction: column;\n  align-items: center;\n  transition: all 0.3s cubic-bezier(.25,.8,.25,1);\n}\n#pwa:hover {\n  text-decoration: none;\n}\nbody:not(.transition) #pwa:hover {\n  box-shadow: 0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22);\n}\n#pwa #pwa-logo-container {\n  height: 128px;\n  width: 128px;\n}\n#pwa:active {\n  opacity: 0.5;\n  box-shadow: 0 2px 2px 0 rgba(0,0,0,.14),0 3px 1px -2px rgba(0,0,0,.2),0 1px 5px 0 rgba(0,0,0,.12);\n}\n#pwa code  {\n  display: block;\n  text-align: center;\n  text-decoration: underline;\n  margin-top: 24px;\n  min-height: 18px;\n  word-break: break-all;\n}\n#pwa > div {\n  margin-top: 16px;\n  min-height: 18px;\n  width: 100%;\n  text-align: center;\n}\n#pwa .dates {\n  font-style: italic;\n  font-size: 12px;\n  line-height: 16px;\n}\n\n/* Lighthouse graph */\n.chart {\n  width: 100%;\n  min-height: 276px;\n  padding: 16px;\n  border-top-style: solid;\n  border-width: 1px;\n  border-color: #ccc;\n  position: relative;\n}\n.chart #chart-missing {\n  position: absolute;\n  text-align: center;\n  top: 45%;\n  width: 100%;\n}\n.chart #chart-missing.fadeIn {\n  display: block;\n}\n#chart_AnnotationChart_borderDiv {\n  height: 244px !important;\n  border-style: none;\n  animation: 0.25s ease-in fadeIn;\n  animation-fill-mode: forwards;\n}\n/* Lighthouse report table */\ntable {\n  margin: 0;\n  font-size: 14px;\n  width: 100%;\n  padding: 0 16px;\n  border-spacing: 0;\n  border-collapse: collapse;\n  border-style: hidden;\n}\nth {\n  color: #1976D2;\n  height: 64px;\n  line-height: 64px;\n  font-weight: 500;\n  text-transform: uppercase;\n}\nth:nth-child(1),\ntd:nth-child(1) {\n  text-align: left;\n  padding-left: 16px;\n}\nth:nth-child(2),\ntd:nth-child(2) {\n  width: 60px;\n  text-align: right;\n  padding-right: 16px;\n}\ntr {\n  height: 48px;\n  color: #333;\n  border-width: 1px;\n  border-color: #eee;\n  border-top-style: solid;\n}\ntr:last-child td {\n  height: 56px;\n  padding-bottom: 8px;\n}\n\n/* Add PWA */\n.form-group {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-orient: vertical;\n  -webkit-box-direction: normal;\n      -ms-flex-direction: column;\n          flex-direction: column;\n  margin: 8px;\n  height: 120px;\n  justify-content: space-between;\n  font-size: 16px;\n  font-weight: 600;\n}\n.form-input {\n  height: 34px;\n  font-size: 16px;\n  font-weight: 600;\n  border-radius: 2px;\n  background: #FFFFFF;\n  border: 0px solid lightgray;\n  outline: none;\n}\n.error {\n  margin-left: 8px;\n  color: #f07;\n}\n\n/* Serch PWA */\n.search-form {\n  flex-grow: 1;\n}\n.search-form .form-input {\n  margin-left: auto;\n  padding-left: 8px;\n  padding-right: 34px;\n}\n.search-group {\n  position: relative;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  align-items: center;\n  height: 48px;\n  line-height: 48px;\n  padding-left: 8px;\n}\n.search-group input {\n  flex-grow: 1;\n}\n#search-icon {\n  min-width: 0;\n  position: absolute;\n  right: 8px;\n  height: 48px;\n}\n#search-icon svg {\n  height: 48px;\n  width: 28px;\n  fill: #757575;\n}\n.toolbar-icon svg {\n  height: 44px;\n  width: 34px;\n  fill: #FFFFFF;\n}\n.toolbar-button {\n  min-width: 0;\n  position: relative;\n  height: 48px;\n}\n.toolbar-button:active {\n  background: #90CAF9;\n  opacity: 1;\n}\n.toolbar-button:focus {\n  outline:0;\n}\n#newest {\n  margin-left: auto;\n}\n#score {\n  margin-right: auto;\n}\n\n/* Footer */\n.offline-status {\n  position: fixed;\n  bottom: 0;\n  width: 100%;\n  background-color: #dc322f;\n  color: #ffffff;\n  height: 56px;\n  line-height: 56px;\n  padding-left: 20px;\n  pointer-events: none;\n  transform: translateY(56px);\n  opacity: 0;\n  transition-property: transform,opacity;\n  transition-duration: .5s;\n  transition-timing-function: ease-in-out;\n}\n\n/* Footer */\nfooter {\n  position: absolute;\n  width:100%;\n  bottom: 0;\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  -webkit-box-align: center;\n      -ms-flex-align: center;\n          align-items: center;\n  color: white;\n}\nfooter ul {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -ms-flex-flow: row;\n      flex-flow: row;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  padding: 0;\n}\nfooter ul li:first-child {\n  border-left: none;\n}\nfooter li {\n  display: block;\n  padding: 0 1em;\n  border-left: 1px solid #fff;\n  line-height: 24px;\n  height: 24px;\n}\nfooter ul li a {\n  -ms-flex-flow: row wrap;\n      flex-flow: row wrap;\n  -webkit-box-pack: center;\n      -ms-flex-pack: center;\n          justify-content: center;\n  text-align: center;\n  text-decoration: none;\n  font-weight: 400;\n  color: #fff;\n  padding: 0;\n  cursor: pointer;\n}\nfooter #github-logo {\n  vertical-align: top;\n  width: 24px;\n  height: 24px;\n}\n\n.page-holder {\n  position: relative;\n  display: flex;\n}\n\n.page-loader {\n  position: absolute;\n  width: 100%;\n  top: 120px;\n  background: #F5F5F5;\n  transition: opacity 0.3s ease-in-out;\n  z-index: -1;\n}\n\n/* Pager */\n.pager {\n  height: 48px;\n  width: 200px;\n  margin: 16px auto;\n}\n\n.pager svg {\n  fill: #1976D2;\n}\n\n.previous, .next {\n  transition: opacity 0.2s ease-in-out;\n}\n\n.previous {\n  float: left;\n}\n\n.next {\n  float: right;\n}\n\n/* Controls Offline and SignedIn Behavirous */\nmain.transition,\nbody[offline] .offline-aware:not([cached]),\nbody:not([signedIn]) .signin-aware {\n  opacity: 0.3;\n  pointer-events: none;\n  box-shadow: none;\n}\n\nbody[offline] .offline-status {\n  transform: translateY(0);\n  opacity: 1;\n}\n\nbody:not([signedIn]) .auth-button-label-logout,\nbody[signedIn] .auth-button-label-login {\n  display: none;\n}\n\n.pager a {\n  color: #0D47A1;\n  text-decoration: none;\n}\n/* Flex List of PWAs */\n.items {\n  display: -webkit-box;\n  display: -ms-flexbox;\n  display: flex;\n  -webkit-flex-wrap: wrap;\n  flex-wrap: wrap;\n}\n.transition .items {\n  opacity: 0;\n}\n.item a {\n  text-decoration: none;\n  display: block;\n  width: 100%;\n  height: 100%;\n}\n.items .item {\n  -webkit-box-flex: 1;\n      -ms-flex: 1 0 280px;\n          flex: 1 0 280px;\n  background: white;\n  color: #171e42;\n  margin:  8px;\n  max-width: calc(100% - 16px);\n}\n\n/* Fixes last flex row\n   min-widt: 280*colums+16*(colums+1) */\n@media (min-width: 608px) {\n  .items .item {\n    max-width: calc(50% - 16px);\n  }\n}\n@media (min-width: 904px) {\n  .items .item {\n    max-width: calc(33.33333% - 16px);\n  }\n}\n@media (min-width: 1200px) {\n  .items .item {\n    max-width: calc(25% - 16px);\n  }\n}\n@media (min-width: 1496px) {\n  .items .item {\n    max-width: calc(20% - 16px);\n  }\n}\n@media (min-width: 1792px) {\n  .items .item {\n    max-width: calc(16.66667% - 16px);\n  }\n}\n@media (min-width: 2088px) {\n  .items .item {\n    max-width: calc(14.2857142857% - 16px);\n  }\n}\n@media (min-width: 2384px) {\n  .items .item {\n    max-width: calc(14.2857142857% - 16px);\n  }\n}\n\n/* Styles for highlighted JSON */\npre {\n  tab-size: 2;\n  padding: 16px;\n  margin: 0;\n  overflow-x: auto;\n}\n.string { color: #0288D1; }\n.number { color: red; }\n.boolean { color: #FF5252; }\n.null { color: magenta; }\n.key { color: #212121; }\n\n.toolbar {\n  width: 100%;\n  display: flex;\n  margin: 0 8px;\n}\n\n.toolbar > div {\n  width: 50%;\n}\n\n.pwa-count {\n  font-weight: 600;\n  height: 50px;\n  line-height: 22px;\n  text-align: end;\n  padding-right: 5px;\n}\n\n/* Notifications */\n.notifications {\n  position: absolute;\n  font-size: 16px;\n  bottom: 24px;\n  right: 10px;\n}\n.notifications svg {\n  position: absolute;\n  top: -6px;\n  right: 48px;\n  fill: #FFFFFF;\n}\n.switch-label {\n  position: relative;\n  display: block;\n  height: 20px;\n  width: 44px;\n  background: #898989;\n  border-radius: 100px;\n  cursor: pointer;\n  transition: all 0.3s ease;\n}\n\n.switch-label:after {\n  position: absolute;\n  left: -2px;\n  top: -3px;\n  display: block;\n  width: 26px;\n  height: 26px;\n  border-radius: 100px;\n  background: #fff;\n  content: '';\n  transition: all 0.3s ease;\n}\n\n.switch-label:active:after { transform: scale(1.15, 0.85); }\n\n.switch:checked ~ label:after {\n  left: 20px;\n  background: #FF5722;\n}\n\n.switch:checked ~ label {\n  background: #FFCCBC;\n}\n\n.switch:disabled ~ label {\n  background: #d5d5d5;\n  pointer-events: none;\n}\n\n.switch:disabled ~ label:after { background: #bcbdbc; }\n\n#notifications_active {\n  visibility: hidden;\n}\n.switch:checked ~ #notifications_active {\n  visibility: visible;\n}\n.switch:checked ~ #notifications_off {\n  visibility: hidden;\n}\n\n.hidden {\n  display: none;\n}\n\n.fadeIn {\n  animation: 0.25s ease-in fadeIn;\n  animation-fill-mode: forwards;\n}\n\n@keyframes fadeIn {\n  0% { opacity: 0 }\n  100% { opacity: 1 }\n}\n\n.fadeOut {\n  animation: 0.25s ease-out fadeOut;\n  animation-fill-mode: forwards;\n}\n\n@keyframes fadeOut {\n  0% { opacity: 1 }\n  100% { opacity: 0 }\n}\n\n/* Loader */\n.loader {\n  text-align: center;\n  display: block;\n  height: 10px;\n  white-space: nowrap;\n  z-index: 1;\n}\n\n.loader-dot {\n  position: relative;\n  display: inline-block;\n\n  height: 10px;\n  width: 10px;\n  margin: 2px;\n  border-radius: 100%;\n  background-color: #000;\n  opacity: 0.6;\n\n  box-shadow: 2px 2px 2px 1px rgba(0, 0, 0, .2);\n  will-change: transform;\n  animation: loader-dots 2s infinite;\n}\n\n.loader .loader-dot:nth-child(1) {\n  animation-delay: 0s;\n}\n\n.loader .loader-dot:nth-child(2) {\n  animation-delay: .1s;\n}\n\n.loader .loader-dot:nth-child(3) {\n  animation-delay: .2s;\n}\n\n@keyframes loader-dots {\n  0%, 100% {\n    transform: scale(.7);\n    opacity: 0.5;\n  }\n\n  50% {\n    transform: scale(.8);\n    opacity: 0.9;\n  }\n}\n/* Theme */\n.primary-color {\n  color: #2196F3;\n}\n.primary-background\n{\n  background: #2196F3;\n}\n.dark-primary-color {\n  color: #1976D2;\n}\n.dark-primary-background\n{\n  background: #1976D2;\n}\n.accent-color {\n  color: #FF5722;\n}\n\n.activetab,\n.accent-border {\n  border-color: #FF5722;\n}\n.button-primary,\n.accent-background {\n  background: #FF5722;\n}\n.link-disabled {\n  pointer-events: none;\n}\n"
  },
  {
    "path": "public/favicons/browserconfig.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n<browserconfig>\r\n  <msapplication>\r\n    <tile>\r\n      <square150x150logo src=\"/favicons/mstile-150x150.png\"/>\r\n      <TileColor>#7cc0ff</TileColor>\r\n    </tile>\r\n  </msapplication>\r\n</browserconfig>\r\n"
  },
  {
    "path": "public/google3915c2aaf77f961f.html",
    "content": "google-site-verification: google3915c2aaf77f961f.html"
  },
  {
    "path": "public/humans.txt",
    "content": "# humanstxt.org/\n# The humans responsible & technology colophon\n\n# TEAM\n\n    Julian Toledo -- @juliantoledo\n    Michael Stillwell -- @ithinkihaveacat\n    Andre Bandarra -- @andreban\n    Alberto Medina -- @amedina\n\n# THANKS\n\n    Ade Oshineye -- @ade_oshineye\n\n# TECHNOLOGY COLOPHON\n\n    CSS3, HTML5, GoogleChrome sw-toolbox\n    Node, Google App Engine, GoogleChrome Lighthouse\n\n    Source: https://github.com/GoogleChrome/gulliver\n"
  },
  {
    "path": "public/js/analytics.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport default class Analytics {\n  constructor(window, config) {\n    this.navigator = window.navigator;\n    this.window = window;\n    this.config = config;\n    this._init();\n    this._setupA2HTracking();\n  }\n\n  _init() {\n    // Setup Tracking if analytics is not loaded yet.\n    if (!this.window.ga) {\n      this.window.ga = (...args) => {\n        (this.window.ga.q = this.window.ga.q || []).push(args);\n      };\n    }\n\n    this.window.ga('create', this.config.ga_id, 'auto');\n    this.window.ga('set', 'transport', 'beacon');\n  }\n\n  /**\n   * Setup a listener to track Add to Homescreen events.\n   */\n  _setupA2HTracking() {\n    this.window.addEventListener('beforeinstallprompt', e => {\n      e.userChoice.then(choiceResult => {\n        this.window.ga('send', 'event', 'A2H', choiceResult.outcome);\n      });\n    });\n  }\n\n  trackOutboundClick(url) {\n    this.window.ga('send', 'event', 'outbound', 'click', url, {transport: 'beacon'});\n  }\n\n  trackPageView(url) {\n    this.window.ga('set', 'page', url);\n    this.window.ga('set', 'dimension1', this.navigator.onLine);\n    this.window.ga('send', 'pageview');\n  }\n}\n"
  },
  {
    "path": "public/js/chart.js",
    "content": "/**\n * copyright 2015-2016, google, inc.\n * licensed under the apache license, version 2.0 (the \"license\");\n * you may not use this file except in compliance with the license.\n * you may obtain a copy of the license at\n *\n *    http://www.apache.org/licenses/license-2.0\n *\n * unless required by applicable law or agreed to in writing, software\n * distributed under the license is distributed on an \"as is\" basis,\n * without warranties or conditions of any kind, either express or implied.\n * see the license for the specific language governing permissions and\n * limitations under the license.\n */\n\n/* global google */\n/* eslint-env browser */\nimport Loader from './loader';\n\n/**\n * Use to make the API request to get the Lighthouse chart data for a PWA.\n */\nexport default class Chart {\n\n  constructor(config) {\n    this.chartElement = config.chartElement;\n    this.url = config.url;\n    this.loader = new Loader(this.chartElement, 'dark-primary-background');\n  }\n\n  _loadChartsApi() {\n    return new Promise((resolve, reject) => {\n      const chartScript = document.getElementById('google-chart');\n      if (chartScript) {\n        if (window.google) {\n          resolve(window.google);\n        } else {\n          chartScript.addEventListener('load', _ => resolve(window.google));\n        }\n      } else {\n        const script = document.createElement('script');\n        script.id = 'google-chart';\n        script.defer = true;\n        script.src = 'https://www.gstatic.com/charts/loader.js';\n        script.onload = _ => resolve(window.google);\n        script.onerror = reject;\n        document.head.appendChild(script);\n      }\n    });\n  }\n\n  load() {\n    this.loader.show();\n    this._loadChartsApi().then(google => {\n      google.charts.load('45.2', {packages: ['annotationchart']});\n      google.charts.setOnLoadCallback(this.drawChart.bind(this));\n    });\n  }\n\n  drawChart() {\n    if (!this.url) {\n      return;\n    }\n    const pagewith = Math.max(document.documentElement.clientWidth, window.innerWidth || 0);\n    fetch(this.url)\n      .then(response => response.json())\n      .then(jsonData => {\n        // Create our data table out of JSON data loaded from server.\n        const data = new google.visualization.DataTable(jsonData);\n        if (data.getNumberOfRows() > 0) {\n          const chart = new google.visualization.AnnotationChart(this.chartElement);\n          const options = {\n            height: 242,\n            displayAnnotations: false,\n            displayRangeSelector: false,\n            displayZoomButtons: (pagewith > 420),\n            legendPosition: 'newRow',\n            thickness: 4,\n            min: 0,\n            max: 100\n          };\n          chart.draw(data, options);\n          this.loader.hide();\n        } else {\n          this.loader.hide();\n          const missingChart = this.chartElement.querySelector('div#chart-missing');\n          missingChart.classList.add('fadeIn');\n        }\n      })\n      .catch(err => {\n        this.loader.hide();\n        const missingChart = document.getElementById('chart-missing');\n        missingChart.classList.add('fadeIn');\n        console.error('There was an error drawing the chart!', err);\n      });\n  }\n}\n"
  },
  {
    "path": "public/js/event-target.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport default class EventTarget {\n  constructor() {\n    this._listeners = new Map();\n  }\n\n  addEventListener(type, callback) {\n    let typeListeners = this._listeners.get(type);\n    if (!typeListeners) {\n      typeListeners = new Set();\n      this._listeners.set(type, typeListeners);\n    }\n    typeListeners.add(callback);\n  }\n\n  removeEventListener(type, callback) {\n    const typeListeners = this._listeners.get(type);\n    if (!typeListeners) {\n      return;\n    }\n    typeListeners.delete(callback);\n  }\n\n  getEventListeners(type) {\n    return this._listeners.get(type);\n  }\n\n  dispatchEvent(event) {\n    if (!event.type) {\n      return;\n    }\n\n    const typeListeners = this._listeners.get(event.type);\n    if (!typeListeners) {\n      return;\n    }\n\n    typeListeners.forEach(callback => callback(event));\n  }\n}\n"
  },
  {
    "path": "public/js/gapi.es6.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\n/**\n * Returns a Promise that fulfills to `window.gapi`. Note that this function\n * will probably create the global properties `gapiReady` and `gapiResolve`.\n *\n * @param {typeof window} context\n * @param {typeof document} doc\n * @return {Promise<typeof window.gapi>}\n */\nexport function gapi(context = window, doc = document) {\n  return context.gapiReady || new Promise(resolve => {\n    // Adapted from GA embed code\n    const c = 'gapiResolve';\n    const s = doc.createElement('script');\n    const p = doc.getElementsByTagName('script')[0];\n    s.async = 1;\n    s.src = `https://apis.google.com/js/api.js?onload=${c}`;\n    p.parentNode.insertBefore(s, p);\n    context[c] = () => resolve(window.gapi);\n  });\n}\n\n/**\n * @template T\n * @param {string} name the library to load\n * @return {Promise<T>} resolves to window.gapi[name]\n */\nexport function gapiLoad(name) {\n  return gapi().then(g => {\n    return new Promise(resolve => {\n      g.load(name, () => resolve(g[name]));\n    });\n  });\n}\n\n/**\n * Promise'd version of [`gapi.client.load`](https://developers.google.com/api-client-library/javascript/reference/referencedocs#gapiclientloadname--------version--------callback).\n *\n * @template T\n * @param {string} name the API client to load\n * @param {string} [version=\"v1\"] version\n * @return {Promise<T>} resolves to gapi.client[name]\n */\nexport function clientLoad(name, version) {\n  version = version ? version : 'v1';\n  return gapiLoad('client').then(client => {\n    return new Promise(resolve => {\n      client.load(name, version, () => resolve(client[name]));\n    });\n  });\n}\n\n/**\n * Promise'd version of [`gapi.auth2.init`](https://developers.google.com/identity/sign-in/web/reference#gapiauth2initparams).\n *\n * @param {any} params https://developers.google.com/identity/sign-in/web/reference#gapiauth2initparams\n * @return {Promise<gapi.auth2.GoogleAuth>} Promise resolving to an initialized gapi.auth2.GoogleAuth object\n */\nexport function authInit(params) {\n  return gapiLoad('auth2').then(auth2 => {\n    /* Ideally we'd just return `auth2.init(params)` here, but\n     * instead we need to work around a few bugs and surprises in\n     * `auth2.init()` and the \"Promise\" it returns.\n     */\n    return new Promise(resolve => {\n      auth2.init(params).then(t => {\n        t.then = null;\n        resolve(t);\n      });\n    });\n  });\n}\n"
  },
  {
    "path": "public/js/gulliver-config.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport default class Config {\n  constructor(element) {\n    if (!element) {\n      console.log('%cConfig not found', 'color:red');\n      return;\n    }\n    const configJSON = JSON.parse(element.innerHTML);\n    Object.assign(this, configJSON);\n  }\n\n  static from(element) {\n    return new Config(element);\n  }\n}\n"
  },
  {
    "path": "public/js/gulliver.es6.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Generate gulliver.js from this file via `npm prestart`. (`npm start` will run\n * `prestart` automatically.)\n */\n\n/* eslint-env browser */\n\n// A Promise polyfill, as used by\n// https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/Promise/config.json\nimport 'yaku/dist/yaku.browser.global.min.js';\n// A fetch polyfill, as used by\n// https://github.com/Financial-Times/polyfill-service/blob/master/polyfills/fetch/config.json\nimport 'whatwg-fetch/fetch';\n\nimport Messaging from './messaging';\nimport NotificationCheckbox from './ui/notification-checkbox';\nimport Config from './gulliver-config';\nimport SignIn from './signin';\nimport OfflineSupport from './offline-support';\nimport {ShareButton} from './ui/share-button';\nimport {SignInButton, SignOutButton} from './ui/signin-button';\nimport Analytics from './analytics';\nimport Router from './routing/router';\nimport Route from './routing/route';\nimport Shell from './shell';\nimport {LoaderTransitionStrategy} from './routing/transitions';\nimport PwaForm from './pwa-form';\nimport Chart from './chart';\nimport SearchInput from './search-input';\n\nconst CHART_BASE_URLS = {\n  lighthouse: 'https://web-performance-dot-pwa-directory.appspot.com/lighthousereport/PWAID?graph=true',\n  psi: 'https://web-performance-dot-pwa-directory.appspot.com/pagespeedreport/PWAID?graph=true',\n  wpt: 'https://web-performance-dot-pwa-directory.appspot.com/webpagetestreport/PWAID?graph=true'\n};\n\nclass Gulliver {\n  constructor() {\n    this.config = Config.from(document.querySelector('#config'));\n    this.shell = new Shell(document);\n    this.router = new Router(window, document.querySelector('main'));\n    this.offlineSupport = new OfflineSupport(window, this.router);\n\n    SearchInput.setupSearchElements(this.router);\n\n    // Setup share button\n    this.shareButton = new ShareButton(window, document.querySelector('#share-button'));\n\n    // Setup SignIn\n    this.signIn = new SignIn(window, this.config);\n    this.signInButton =\n        new SignInButton(window, this.signIn, document.querySelector('#signin-button'));\n    this.signOutButton =\n        new SignOutButton(window, this.signIn, document.querySelector('#signout-button'));\n\n    // Setup Analytics\n    this.analytics = new Analytics(window, this.config);\n    this.analytics.trackPageView(window.location.href);\n    this.router.addEventListener('navigateoutbound', e => {\n      this.analytics.trackOutboundClick(e.detail.url);\n    });\n\n    this.router.addEventListener('navigate', e => {\n      this.analytics.trackPageView(e.detail.url);\n      this.shell.onRouteChange(e.detail.route);\n      this.offlineSupport.markAsCached(document.querySelectorAll('.offline-aware'));\n    });\n\n    this._setupRoutes();\n    this.setupBacklink();\n    this.setupServiceWorker();\n    this.setupMessaging();\n  }\n\n  _addRoute(regexp, transitionStrategy, onRouteAttached, shellState) {\n    const route = new Route(regexp, transitionStrategy, onRouteAttached);\n    this.shell.setStateForRoute(route, shellState);\n    this.router.addRoute(route);\n  }\n\n  _setupRoutes() {\n    const transitionStrategy = new LoaderTransitionStrategy(window);\n    // Route for `/pwas/add`.\n    const setupPwaForm = () => {\n      const pwaForm = new PwaForm(window, this.signIn);\n      pwaForm.setup();\n    };\n\n    // Link search-input value to search query paramter\n    const setupSearchInput = () => {\n      const urlParams = new URLSearchParams(window.location.search);\n      document.querySelector('#search-input').value = urlParams.get('query');\n    };\n\n    this._addRoute(/\\/pwas\\/add/, transitionStrategy, [setupPwaForm, setupSearchInput], {\n      showTabs: false,\n      backlink: true,\n      subtitle: true,\n      search: true\n    });\n\n    const setupCharts = () => {\n      const generateChartConfig = chartElement => {\n        const pwaId = chartElement.getAttribute('pwa');\n        const type = chartElement.getAttribute('type');\n        const url = CHART_BASE_URLS[type].replace('PWAID', pwaId);\n        return {chartElement: chartElement, url: url};\n      };\n      const charts = Array.from(document.getElementsByClassName('chart'));\n      charts.forEach(chart => new Chart(generateChartConfig(chart)).load());\n    };\n\n    // Route for `/pwas/score`.\n    this._addRoute(/\\/pwas\\/score/, transitionStrategy, setupSearchInput, {\n      showTabs: true,\n      backlink: false,\n      subtitle: true,\n      search: true,\n      currentTab: 'score'\n    });\n\n    // Route for `/pwas/newest`.\n    this._addRoute(/\\/pwas\\/newest/, transitionStrategy, setupSearchInput, {\n      showTabs: true,\n      backlink: false,\n      subtitle: true,\n      search: true,\n      currentTab: 'newest'\n    });\n\n    // Route for `/pwas/[id]`. Allow most characters (but will only ever be encodedURIComponent).\n    this._addRoute(/\\/pwas\\/.+/, transitionStrategy, [setupCharts, setupSearchInput], {\n      showTabs: false,\n      backlink: true,\n      subtitle: true,\n      search: true\n    });\n\n    // Route for `/?search=`.\n    this._addRoute(/\\/pwas\\/search\\?query/, transitionStrategy, setupSearchInput, {\n      showTabs: false,\n      backlink: true,\n      subtitle: true,\n      search: true\n    });\n\n    // Route for `/`.\n    this._addRoute(/.+/, transitionStrategy, setupSearchInput, {\n      showTabs: true,\n      backlink: false,\n      subtitle: true,\n      search: true,\n      currentTab: 'installable'\n    });\n\n    this.router.setupInitialRoute();\n  }\n\n  /**\n   * Register service worker.\n   */\n  setupServiceWorker() {\n    if ('serviceWorker' in navigator) {\n      navigator.serviceWorker.register('/sw.js').then(r => {\n        console.log('REGISTRATION', r);\n      });\n    } else {\n      console.log('SW not registered; navigator.serviceWorker is not available');\n    }\n  }\n\n  /**\n   * Setup/configure Firebase Cloud Messaging.\n   */\n  setupMessaging() {\n    try {\n      const NEW_APPS_TOPIC = 'new-apps';\n      const firebaseMsgSenderId = this.config.firebase_msg_sender_id;\n      const checkbox = document.getElementById('notifications');\n      const messaging = new Messaging(firebaseMsgSenderId);\n      // eslint-disable-next-line no-unused-vars\n      const notificationCheckbox = new NotificationCheckbox(messaging, checkbox, NEW_APPS_TOPIC);\n    } catch (e) {\n      console.log(e);\n    }\n  }\n\n /**\n  * Setup/configure header section-title's backlink chevron\n  */\n  setupBacklink() {\n    document.querySelector('a#backlink').addEventListener('click', _ => {\n      if (document.referrer.length > 0 &&\n          !document.referrer.startsWith(document.location.origin)) {\n        this.router.navigate('/');\n        return;\n      }\n      window.history.back();\n    });\n  }\n}\n\nwindow.gulliver = new Gulliver();\n"
  },
  {
    "path": "public/js/loader.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nimport './util/requestIdleCallback';\n\nconst FADE_OUT_ANIMATION_LENGTH = 500;\n\n/**\n * A CSS only loader showing three dots.\n */\nclass Loader {\n\n  /**\n   * Create a new loader.\n   *\n   * @param container {HTMLElement} the element containing the loader\n   * @param style {String} optional hex color or css class for styling the loader\n   */\n  constructor(container, style) {\n    this.style = style || '';\n    this.container = container;\n  }\n\n  /**\n   * addLoader adds a CSS loader to the given element.\n   *\n   * @param container {HTMLElement} the element containing the loader.\n   */\n  show() {\n    const loader = document.createElement('div');\n    loader.style['align-items'] = 'center';\n    loader.classList.add('loader');\n    for (let i = 0; i < 3; i++) {\n      const dot = document.createElement('div');\n      dot.classList.add('loader-dot');\n      if (this.style.startsWith('#')) {\n        dot.style['background-color'] = this.style;\n      } else if (this.style) {\n        dot.classList.add(this.style);\n      }\n      loader.appendChild(dot);\n    }\n    this.container.appendChild(loader);\n  }\n\n  /**\n   * removeLoader removes a CSS loader from the given element.\n   *\n   * @param container {HTMLElement} the element containing the loader.\n   */\n  hide() {\n    const loaders = this.container.querySelectorAll('.loader');\n    loaders.forEach(loader => {\n      loader.classList.add('fadeOut');\n      window.requestIdleCallback(() => loader.remove(), {\n        timeout: FADE_OUT_ANIMATION_LENGTH\n      });\n    });\n  }\n}\nexport default Loader;\n"
  },
  {
    "path": "public/js/messaging.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nimport 'whatwg-fetch/fetch'; // Imports `fetch` polyfill\nimport firebase from 'firebase/app';\nimport 'firebase/messaging';\n\nconst SUBSCRIBE_ENDPOINT = '/api/notifications/subscribe';\nconst UNSUBSCRIBE_ENDPOINT = '/api/notifications/unsubscribe';\nconst TOPICS_ENDPOINT = '/api/notifications/topics';\n\nconst ERROR_PERMISSION_BLOCKED = 'messaging/permission-blocked';\nconst ERROR_NOTIFICATIONS_BLOCKED = 'messaging/notifications-blocked';\n// const ERROR_PERMISSION_DEFAULT = 'messaging/permission-default';\n\nexport default class Messaging {\n  constructor(messagingSenderId) {\n    const config = {\n      messagingSenderId: messagingSenderId\n    };\n    firebase.initializeApp(config);\n  }\n\n  /**\n   * Fetches from url, adding token to the request body\n   * as a JSON\n   *\n   * @param url - the url to fetch from\n   * @param token - the token to be sent to the server\n   */\n  _postWithToken(url, token) {\n    return fetch(url, {\n      method: 'POST',\n      headers: {\n        'Accept': 'application/json',\n        'Content-Type': 'application/json'\n      },\n      body: JSON.stringify({token: token})\n    });\n  }\n\n  _checkBlockedNotification(err) {\n    return err.code === ERROR_PERMISSION_BLOCKED ||\n            err.code === ERROR_NOTIFICATIONS_BLOCKED;\n  }\n  /**\n   * Enables Notifications to a topic.\n   * Will ask user permission, if needed and then subscribe\n   * to a topic on the server.\n   *\n   * @param topic - The topic to subscribe to\n   * @returns a Promise\n   */\n  subscribe(topic) {\n    console.log('Subscribing to: ' + topic);\n    const messaging = firebase.messaging();\n    return messaging.requestPermission()\n      .then(() => {\n        return messaging.getToken();\n      })\n      .then(token => {\n        console.log(token);\n        const url = SUBSCRIBE_ENDPOINT + '/' + topic;\n        return this._postWithToken(url, token);\n      })\n      .catch(err => {\n        if (this._checkBlockedNotification(err)) {\n          err.blocked = true;\n        }\n        return Promise.reject(err);\n      });\n  }\n\n  /**\n   * Disables Notifications from a topic.\n   *\n   * @param topic - The topic to unsubscribe from.\n   * @returns Promise\n   */\n  unsubscribe(topic) {\n    console.log('Unsubscribing from: ' + topic);\n    const messaging = firebase.messaging();\n    return messaging.getToken()\n      .then(token => {\n        const url = UNSUBSCRIBE_ENDPOINT + '/' + topic;\n        return this._postWithToken(url, token);\n      }).catch(err => {\n        if (this._checkBlockedNotification(err)) {\n          err.blocked = true;\n        }\n        return Promise.reject(err);\n      });\n  }\n\n  /**\n   * Gets all topics user has subscribed for\n   *\n   * @returns Promise<Array<String>>\n   */\n  getSubscriptions() {\n    const messaging = firebase.messaging();\n    return messaging.getToken()\n      .then(token => {\n        if (!token) {\n          return Promise.resolve([]);\n        }\n        const url = TOPICS_ENDPOINT + '?token=' + token;\n        return fetch(url, {headers: {Accept: 'application/json'}})\n          .then(response => {\n            if (response.status !== 200) {\n              return [];\n            }\n            return response.json()\n              .then(json => {\n                return json.subscriptions;\n              });\n          });\n      })\n      .catch(err => {\n        console.error('Error fetching subscriptions: ', err);\n        return Promise.resolve([]);\n      });\n  }\n\n  /**\n   * Checks if user is subscribed to a topic.\n   * @param topic - The topic to check for subscriptions.\n   * @returns Promise<Boolean> - true if subscribed, false if not.\n   */\n  isSubscribed(topic) {\n    return this.getSubscriptions()\n      .then(subscriptions => {\n        return subscriptions.indexOf(topic) >= 0;\n      });\n  }\n\n  isNotificationBlocked() {\n    const messaging = firebase.messaging();\n    return messaging.getToken()\n      .then(_ => {\n        return false;\n      })\n      .catch(err => {\n        if (err.code === ERROR_NOTIFICATIONS_BLOCKED) {\n          return true;\n        }\n        return Promise.reject(err);\n      });\n  }\n}\n"
  },
  {
    "path": "public/js/offline-support.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport default class OfflineSupport {\n\n  constructor(window, router) {\n    this.window = window;\n    this.router = router;\n    this._setupEventhandlers();\n  }\n\n  /**\n   * All elements with class .gulliver-online-aware will:\n   * have an 'online' dataset property that reflects the current online state.\n   * receive a 'change' event whenever the state changes.\n   */\n  _setupEventhandlers() {\n    const body = this.window.document.querySelector('body');\n    this.window.addEventListener('online', () => {\n      body.removeAttribute('offline');\n    });\n\n    this.window.addEventListener('offline', () => {\n      body.setAttribute('offline', 'true');\n      this.markAsCached(this.window.document.querySelectorAll('.offline-aware'));\n    });\n\n    const onLine = this.window.navigator.onLine;\n    if (onLine !== undefined && !onLine) {\n      body.setAttribute('offline', 'true');\n    }\n  }\n\n  /**\n   * Check if a Url is navigable.\n   * @param url the url to be checke for availability\n   * @returns true if the user is online or the URL is cached\n   */\n  isAvailable(href) {\n    if (!href || this.window.navigator.onLine) return Promise.resolve(true);\n    return caches.match(href)\n      .then(response => response.status === 200)\n      .catch(() => false);\n  }\n\n  /**\n   * Checks if the href on the anchor is available in the cached\n   * and marks the element with the cached attribute.\n   *\n   * If the url is available, the `cached` attribute is added with\n   * the value `true`. Otherwise, the `cached` attribute is removed.\n   * @param {@NodeList} a list of anchors.\n   */\n  markAsCached(anchors) {\n    anchors.forEach(anchor => {\n      if (!anchor.href) {\n        return;\n      }\n      const route = this.router.findRoute(anchor.href);\n      if (!route) {\n        return;\n      }\n      const contentHref = route.getContentOnlyUrl(anchor.href);\n      this.isAvailable(contentHref).then(available => {\n        if (available) {\n          anchor.setAttribute('cached', 'true');\n          return;\n        }\n        anchor.removeAttribute('cached');\n      });\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/pwa-form.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nimport Loader from './loader';\n\n// SVG from https://svgsilh.com/svg/2026645.svg\nconst SVG = '<svg version=\"1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"64\" height=\"64\" viewBox=\"0 0 1280 1280\"><path d=\"M610 .6c-46.3 3.1-74.1 6.8-109 14.5-86.5 19.1-171.8 58.2-243 111.4C117.6 231.5 26.3 387.8 5.1 560 1.9 585.6 1 598.4.3 626.5c-2.1 90.7 15.1 180.3 50.9 264.7 7.4 17.5 26.2 54.8 35.8 71.3 92.4 157.8 248.9 269.6 427 305 152.2 30.3 308.8 5 442.6-71.4 37.7-21.5 72.2-46 104.9-74.5 15.2-13.2 45.3-43.4 59.2-59.2 118.4-135.1 174.3-310.7 155.8-489.3-6.7-63.9-22.5-125-48-185.1-7.2-16.8-26.1-54.4-35.5-70.5C1097.8 155 935.5 41.9 751 9.6c-35.2-6.2-62.4-8.6-103-9-17.9-.2-35-.2-38 0zM667.5 58C798 64.9 914.4 110.6 1015 194.4c17 14.2 52.3 49 66.9 66.1 69.1 80.8 115.1 178.7 132.6 282.3 20.4 120.4 2.3 244.7-51.5 353.7-29 58.8-63.9 107.3-111 154.6-34.1 34.2-64.9 58.8-104.4 83.4-78.2 48.9-163.6 77.3-258.6 86.1-25.2 2.4-72.8 2.4-98 0-99.3-9.1-187.6-39.5-268.5-92.4C212.2 1056.1 129.4 949.5 87.9 826c-41.4-123.4-40.8-253.5 1.6-376.7 22.7-66 57.7-127.6 104.9-184.3 15.6-18.9 52.1-55.3 71.1-71.1 59.8-49.7 122.7-84.6 195.4-108.4C526.1 64.2 599.4 54.4 667.5 58z\"/><path d=\"M347.2 359L266 412.9l15.7 23.6c9 13.5 16.2 23.4 16.9 23.1.6-.2 24-15.6 52-34.2 28-18.5 64.2-42.5 80.4-53.2 16.2-10.7 29.6-19.6 29.8-19.7.5-.4-31-47.5-31.7-47.5-.3 0-37.2 24.3-81.9 54zM833 328.6c-8.4 13-14.9 23.9-14.4 24.3 3 2.7 164.7 107.2 165.4 106.9 1.1-.4 31.4-46.8 30.9-47.3-1.3-1.2-165.2-107.5-165.8-107.5-.4 0-7.6 10.6-16.1 23.6zM397.5 460.4c-19 4.7-33.9 19.5-39 38.7-1.9 6.9-1.9 19.9 0 26.8 4.5 16.9 18.4 31.7 35.1 37.2 33.8 11.2 69.5-14.5 69.7-50.1.1-9.7-1.7-16.8-6.2-25.5-7-13.6-21.1-24.3-35.9-27.4-6.8-1.4-17.3-1.3-23.7.3zM850.3 460.4c-18.5 4.7-33.2 19-38.7 37.6-1 3.3-1.7 9.1-1.7 14.5 0 15.1 4.1 25.2 14.8 36.5 16 17 40 21.5 61.2 11.5 13.6-6.5 25.7-21.3 29-35.5 1.5-6.6 1.3-19.7-.4-26.3-4.3-16.7-17.5-31-34-36.8-7.5-2.7-22.7-3.4-30.2-1.5zM610 808.6c-1.9.2-8 .9-13.5 1.4-51.4 5.4-104 21.4-157.5 48.2-19.8 9.9-53.2 28.8-57.2 32.5-1.6 1.4-1.7 4.7-1.7 35.5 0 26.4.2 33.9 1.2 33.5.6-.3 7.5-4.8 15.2-10.2 70.7-49 142.7-77.1 213.3-83.5 17.2-1.6 60.5-.8 76.2 1.5 71 10 137 38.6 211.1 91.4 1.9 1.3 1.9.7 1.9-33.5v-34.9l-2.8-1.6c-1.5-.9-8-4.8-14.4-8.7-47.6-29.1-101.5-51.4-149.8-62.2-33.6-7.5-48.6-9.1-86.5-9.5-17.6-.2-33.6-.1-35.5.1z\"/></svg>';\n\nexport default class PwaForm {\n  constructor(window, signIn) {\n    this._window = window;\n    this._signIn = signIn;\n  }\n\n  setup() {\n    console.log('Setting up PWA Form');\n    this._pwaForm = document.querySelector('#pwaForm');\n    if (!this._pwaForm) {\n      console.log('%c#pwaForm not found.', 'color:red');\n      return;\n    }\n\n    this._manifestUrlInput = document.querySelector('#manifestUrl');\n    if (!this._manifestUrlInput) {\n      console.log('%c#manifestUrl input not found.', 'color:red');\n      return;\n    } \n\n    this._pageContainer = document.querySelector('.items');\n    this._loadingTemplate = document.querySelector('#template-load-pwa').\n        content.querySelector('a');\n    this._setupListeners();\n  }\n\n   _addPwa(container, manifestUrl) {\n      console.log(container);\n      const icon = container.querySelector('.icon');\n      const text = container.querySelector('.pwa-name');\n      const loader = new Loader(icon);\n      text.innerText = manifestUrl;\n      loader.show();\n\n      fetch('/api/pwa/add', {\n        method: 'POST',\n        headers: {\n          'Content-Type': 'application/json'\n        },\n        body: JSON.stringify({\n          idToken: this._signIn.idToken,\n          manifestUrl: manifestUrl\n        })\n      })\n      .then(response => response.json())\n      .then(json => {\n        loader.hide();\n        if(json.error) {\n          loader.hide();\n          text.innerText = `Error: ${json.error}`;\n          icon.innerHTML = SVG;\n          return;\n        }\n        text.innerText = json.name;\n        icon.innerText = json.name[0];\n        container.setAttribute('href', '/pwas/' + json.id);\n        container.classList.remove('link-disabled');\n        container.style['background-color'] = json.backgroundColor;\n        icon.style['color'] = json.foregroundColor;\n        text.style['color'] = json.foregroundColor;\n      })\n      .catch(err => {\n        loader.hide();\n        text.innerText = `Error: ${text.innerText}`;\n        icon.innerHTML = SVG;\n        console.log(err);\n      })\n  }\n\n  /**\n   * Sets up a listeners for events.\n   */\n  _setupListeners() {\n    this._pwaForm.addEventListener('submit', event => {\n      event.preventDefault();\n      const newLoading = this._loadingTemplate.cloneNode(true);\n      this._pageContainer.appendChild(newLoading);\n      this._addPwa(newLoading, this._manifestUrlInput.value);\n      this._pwaForm.reset();\n      return false;\n    });\n\n    // Setup listener for the userchange event.\n    this._window.addEventListener('userchange', () => {\n      console.log(this._signIn.signedIn);\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/routing/route.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nimport 'url-polyfill/url-polyfill';\n\nexport default class Route {\n  constructor(matchRegex, transitionStrategy, onAttached) {\n    this._transitionStrategy = transitionStrategy;\n    this._matchRegex = matchRegex;\n    this._onAttached = onAttached;\n  }\n\n  matches(url) {\n    return this._matchRegex.test(url);\n  }\n\n  retrieveContent(url) {\n    const contentUrl = this.getContentOnlyUrl(url);\n    return fetch(contentUrl)\n      .then(response => response.text());\n  }\n\n  transitionOut(container) {\n    this._transitionStrategy.transitionOut(container);\n  }\n\n  transitionIn(container) {\n    this._transitionStrategy.transitionIn(container);\n  }\n\n  onAttached() {\n    if (this._onAttached && Array.isArray(this._onAttached)) {\n      this._onAttached.forEach(onAttached => {\n        onAttached && onAttached();\n      });\n      return;\n    }\n    return this._onAttached && this._onAttached();\n  }\n\n  getContentOnlyUrl(url) {\n    const u = new URL(url);\n    u.searchParams.append('contentOnly', 'true');\n    return u.toString();\n  }\n}\n"
  },
  {
    "path": "public/js/routing/router.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nimport EventTarget from '../event-target';\n\nexport default class Router {\n  constructor(window, container) {\n    this._routes = [];\n    this._window = window;\n    this._container = container;\n    this._document = window.document;\n    this._eventTarget = new EventTarget();\n\n    // Update UI when back is pressed.\n    this._window.addEventListener('popstate', this._updateContent.bind(this));\n    this._takeOverAnchorLinks(this._window.document);\n  }\n\n  findRoute(url) {\n    return this._routes.find(route => route.matches(url));\n  }\n\n  addEventListener(type, callback) {\n    this._eventTarget.addEventListener(type, callback);\n  }\n\n  _updateContent() {\n    const location = this._window.document.location.href;\n    const route = this.findRoute(location);\n    if (!route) {\n      console.error('Url did not match any router: ', location);\n      // TODO: navigate to 404?\n      return;\n    }\n\n    this._window.scrollTo(0, 0);\n    route.transitionOut(this._container);\n    route.retrieveContent(location)\n      .then(content => {\n        this._container.innerHTML = content;\n        route.transitionIn(this._container);\n        this._takeOverAnchorLinks(this._container);\n        route.onAttached();\n        this._dispatchNavigateEvent(location, route);\n      })\n      .catch(err => {\n        console.error('Error getting page content for: ', location, ' Error: ', err);\n      });\n  }\n\n  addRoute(route) {\n    this._routes.push(route);\n  }\n\n  navigate(url) {\n    console.log('Navigating To: ', url);\n    this._window.history.pushState(null, null, url);\n    this._updateContent();\n  }\n\n  setupInitialRoute() {\n    const body = this._document.querySelector('body');\n    if (body.hasAttribute('data-empty-shell')) {\n      this._updateContent();\n      return;\n    }\n    const location = this._document.location.href;\n    const route = this.findRoute(location);\n    this._takeOverAnchorLinks(this._container);\n    route.onAttached();\n  }\n\n  _dispatchNavigateEvent(url, route) {\n    const event = this._document.createEvent('CustomEvent');\n    const detail = {\n      url: url,\n      route: route\n    };\n    event.initCustomEvent(\n        'navigate', /* bubbles */ false, /* cancelable */ false, detail);\n    this._eventTarget.dispatchEvent(event);\n  }\n\n  _dispatchOutboundNavigationEvent(url) {\n    const event = this._document.createEvent('CustomEvent');\n    const detail = {\n      url: url\n    };\n    event.initCustomEvent('navigateoutbound', false, false, detail);\n    this._eventTarget.dispatchEvent(event);\n  }\n\n  _isNotLeftClickWithoutModifiers(e) {\n    return e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey;\n  }\n\n  _takeOverAnchorLinks(root) {\n    root.querySelectorAll('a').forEach(element => {\n      element.addEventListener('click', e => {\n        if (this._isNotLeftClickWithoutModifiers(e)) {\n          return true;\n        }\n\n        // Link does not have an url.\n        if (!e.currentTarget.href) {\n          return true;\n        }\n\n        // Never catch links to external websites.\n        if (!e.currentTarget.href.startsWith(this._window.location.origin)) {\n          this._dispatchOutboundNavigationEvent(e.currentTarget.href);\n          return true;\n        }\n\n        // Check if there's a route for this url.\n        const route = this.findRoute(e.currentTarget.href);\n        if (!route) {\n          return true;\n        }\n\n        e.preventDefault();\n        this.navigate(e.currentTarget.href);\n        return false;\n      });\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/routing/transitions.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\nimport Loader from '../loader';\n\nexport class FadeInOutTransitionStrategy {\n  transitionIn(container) {\n    container.classList.remove('transition');\n  }\n\n  transitionOut(container) {\n    container.classList.add('transition');\n  }\n}\n\nexport class LoaderTransitionStrategy {\n  constructor(window) {\n    this._window = window;\n    const loaderDiv = window.document.querySelector('.page-loader');\n    this._loader = new Loader(loaderDiv);\n  }\n\n  transitionIn(container) {\n    container.classList.remove('transition');\n    this._loader.hide();\n  }\n\n  transitionOut(container) {\n    container.classList.add('transition');\n    this._loader.show();\n  }\n}\n"
  },
  {
    "path": "public/js/search-input.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Generate gulliver.js from this file via `npm prestart`. (`npm start` will run\n * `prestart` automatically.)\n */\n\n/* eslint-env browser */\n\nclass SearchButton {\n /**\n  * Setup/configure search button\n  */\n  setupSearchElements(router) {\n    const eventHandler = event => {\n      event.preventDefault();\n      const searchValue = document.querySelector('#search-input').value;\n      if (searchValue.length === 0) {\n        router.navigate('/');\n      } else {\n        const urlParams = new URLSearchParams(window.location.search);\n        // Only navigate if the search query changes\n        if (searchValue !== urlParams.get('query')) {\n          router.navigate('/pwas/search?query=' + searchValue);\n        }\n        document.querySelector('#search-input').blur();\n      }\n    };\n    document.querySelector('#search').addEventListener('submit', eventHandler);\n  }\n}\n\nexport default new SearchButton();\n"
  },
  {
    "path": "public/js/shell.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport default class Shell {\n  constructor(document) {\n    this._document = document;\n    this._backlink = document.querySelector('#backlink');\n    this._tabs = Array.from(document.querySelectorAll('#installable, #newest, #score, #tabs'));\n    this._subtitle = document.querySelector('#subtitle');\n    this._search = document.querySelector('#search');\n    this._states = new Map();\n  }\n\n  setStateForRoute(route, shellState) {\n    this._states.set(route, shellState);\n  }\n\n  _showElement(element, visible) {\n    if (visible) {\n      element.classList.remove('hidden');\n      return;\n    }\n    element.classList.add('hidden');\n  }\n\n  _updateTab(tab, options) {\n    this._showElement(tab, options.showTabs);\n    if (!options.currentTab) {\n      return;\n    }\n\n    if (tab.id === options.currentTab) {\n      tab.classList.add('activetab');\n      return;\n    }\n    tab.classList.remove('activetab');\n  }\n\n  onRouteChange(route) {\n    const options = this._states.get(route);\n    this._showElement(this._backlink, options.backlink);\n    this._showElement(this._subtitle, options.subtitle);\n    this._showElement(this._search, options.search);\n    this._tabs.forEach(tab => this._updateTab(tab, options));\n  }\n}\n"
  },
  {
    "path": "public/js/signin.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\nimport {authInit} from './gapi.es6.js';\n\nexport default class SignIn {\n  constructor(window, config) {\n    this.window = window;\n    this.config = config;\n    this._init();\n    this._setupEventHandlers();\n  }\n\n  _init() {\n    /* eslint-disable camelcase */\n    const params = {\n      scope: 'profile',\n      client_id: this.config.client_id,\n      fetch_basic_profile: false\n    };\n    /* eslint-enable camelcase */\n\n    return authInit(params).then(auth => {\n      this.auth = auth;\n      this._setupUserChangeEvents(auth);\n      return this;\n    });\n  }\n\n  _setupUserChangeEvents(auth) {\n    this.window.auth = auth; // TODO: Temporary Hack to Make 'ui/client-transition.js' work.\n    // Fire 'userchange' event on page load (not just when status changes)\n    this.window.dispatchEvent(new CustomEvent('userchange', {\n      detail: auth.currentUser.get()\n    }));\n\n    // Fire 'userchange' event when status changes\n    auth.currentUser.listen(user => {\n      window.dispatchEvent(new CustomEvent('userchange', {\n        detail: user\n      }));\n    });\n  }\n\n  get signedIn() {\n    return this.user && this.user.isSignedIn();\n  }\n\n  get user() {\n    if (!this.auth) {\n      return null;\n    }\n    return this.auth.currentUser.get();\n  }\n\n  get idToken() {\n    if (!this.signedIn) {\n      return null;\n    }\n    return this.user.getAuthResponse().id_token;\n  }\n\n  signIn() {\n    if (!this.auth) {\n      console.log('Auth not ready!');\n      return;\n    }\n    this.auth.signIn();\n  }\n\n  signOut() {\n    if (!this.auth) {\n      console.log('Auth not ready!');\n      return;\n    }\n    this.auth.signOut();\n  }\n\n  /**\n   * All elements with class .gulliver-signedin-aware will:\n   * have a 'signedin' dataset property that reflects the current signed in state.\n   * receive a 'change' event whenever the state changes.\n   */\n  _setupEventHandlers() {\n    const body = this.window.document.querySelector('body');\n    this.window.addEventListener('userchange', e => {\n      const user = e.detail;\n      if (user.isSignedIn()) {\n        body.setAttribute('signedIn', 'true');\n      } else {\n        body.removeAttribute('signedIn');\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/ui/notification-checkbox.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport default class NotificationCheckbox {\n  constructor(messaging, checkbox, topic) {\n    if (!checkbox) {\n      console.error('checkbox parameter cannot be null');\n      return;\n    }\n    this.messaging = messaging;\n    this.checkbox = checkbox;\n    this.topic = topic;\n    this._setupEventListener();\n\n    // Initilize checkbox state.\n    this.messaging.isNotificationBlocked()\n      .then(blocked => {\n        if (blocked) {\n          checkbox.disabled = true;\n          return;\n        }\n\n        this.messaging.isSubscribed(topic)\n          .then(subscribed => {\n            checkbox.checked = subscribed;\n          });\n      });\n  }\n\n  _setupEventListener() {\n    this.checkbox.addEventListener('change', e => {\n      if (e.target.checked) {\n        this.messaging.subscribe(this.topic)\n          .catch(e => {\n            console.error('Error subscribing to topic: ', e);\n            this.checkbox.checked = false;\n            if (e.blocked) {\n              this.checkbox.disabled = true;\n            }\n          });\n        return;\n      }\n      this.messaging.unsubscribe(this.topic)\n        .catch(err => {\n          console.error('Error unsubscribing from topic: ', err);\n          if (err.blocked) {\n            this.checkbox.disabled = true;\n            this.checkbox.checked = false;\n            return;\n          }\n          this.checkbox.checked = true;\n          return;\n        });\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/ui/share-button.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport class ShareButton {\n  constructor(window, element, nameElement) {\n    this.element = element;\n    this._nameElement = nameElement;\n    this._window = window;\n    this._init();\n  }\n\n  _init() {\n    if (!this._window.navigator.share) {\n      return;\n    }\n    this.element.classList.remove('hidden');\n    this._setupEventListeners();\n  }\n\n  _setupEventListeners() {\n    const clickListener = () => {\n      this.share();\n    };\n    this.element.addEventListener('click', clickListener);\n  }\n\n  _getTitle() {\n    const pwaName = this._window.document.querySelector('#pwa-name');\n    if (!pwaName) {\n      return 'PWA Directory';\n    }\n    return pwaName.innerText.trim();\n  }\n\n  share() {\n    const title = this._getTitle();\n    this._window.navigator.share({\n      title,\n      url: this._window.location.href\n    }).catch(err => {\n      console.log(`Share failed, reason: ${err}`);\n    });\n  }\n}\n"
  },
  {
    "path": "public/js/ui/signin-button.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* eslint-env browser */\n\nexport class SignInButton {\n  constructor(window, signIn, element) {\n    this.signIn = signIn;\n    this.element = element;\n    this._window = window;\n    this._setupEventListeners();\n  }\n\n  _setupEventListeners() {\n    // Make SignIn button react to userchange events.\n    this._window.addEventListener('userchange', () => {\n      if (this.signIn.signedIn) {\n        this.element.classList.add('hidden');\n      } else {\n        this.element.classList.remove('hidden');\n      }\n    });\n\n    const clickListener = () => {\n      if (!this.signIn.signedIn) {\n        this.signIn.signIn();\n      }\n    };\n    this.element.addEventListener('click', clickListener);\n  }\n}\n\nexport class SignOutButton {\n  constructor(window, signIn, element) {\n    this.signIn = signIn;\n    this.element = element;\n    this._window = window;\n    this._setupEventListeners();\n  }\n\n  _setupEventListeners() {\n    // Make SignOut button react to userchange events.\n    this._window.addEventListener('userchange', () => {\n      if (this.signIn.signedIn) {\n        this.element.classList.remove('hidden');\n      } else {\n        this.element.classList.add('hidden');\n      }\n    });\n\n    const clickListener = () => {\n      if (this.signIn.signedIn) {\n        this.signIn.signOut();\n      }\n    };\n    this.element.addEventListener('click', clickListener);\n  }\n}\n"
  },
  {
    "path": "public/js/util/requestIdleCallback.js",
    "content": "/*!\n * Copyright 2015 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing\n * permissions and limitations under the License.\n */\n\n/* eslint-env browser */\n\n/*\n * @see https://developers.google.com/web/updates/2015/08/using-requestidlecallback\n */\nwindow.requestIdleCallback = window.requestIdleCallback ||\n  (cb => {\n    return setTimeout(_ => {\n      let start = Date.now();\n      cb({\n        didTimeout: false,\n        timeRemaining: _ => {\n          return Math.max(0, 50 - (Date.now() - start));\n        }\n      });\n    }, 1);\n  });\n\nwindow.cancelIdleCallback = window.cancelIdleCallback ||\n  (id => {\n    clearTimeout(id);\n  });\n"
  },
  {
    "path": "public/manifest.json",
    "content": "{\n\t\"name\": \"PWA Directory\",\n\t\"short_name\": \"PwaDirectory\",\n\t\"description\": \"A Directory of PWAs\",\n\t\"start_url\": \"/?utm_source=homescreen\",\n\t\"icons\": [\n\t\t{\n\t\t\t\"src\": \"\\/favicons\\/android-chrome-192x192.png\",\n\t\t\t\"sizes\": \"192x192\",\n\t\t\t\"type\": \"image\\/png\"\n\t\t},\n\t\t{\n\t\t\t\"src\": \"\\/favicons\\/android-chrome-512x512.png\",\n\t\t\t\"sizes\": \"512x512\",\n\t\t\t\"type\": \"image\\/png\"\n\t\t}\n\t],\n\t\"theme_color\": \"#7cc0ff\",\n\t\"background_color\": \"#7cc0ff\",\n\t\"display\": \"standalone\",\n\t\"scope\": \"/\",\n\t\"share_target\": {\n\t\t\"url_template\": \"/pwas/add?url={url}\"\n\t},\n  \"//\": \"Some browsers will use this to enable push notifications.\",\n  \"//\": \"It is the same for all projects, this is not your project's sender ID\",\n\t\"gcm_sender_id\": \"103953800507\"\n}\n"
  },
  {
    "path": "public/robots.txt",
    "content": "User-Agent: *\nDisallow: \n"
  },
  {
    "path": "public/sw.js",
    "content": "/* eslint-env serviceworker, browser */\n\n// sw-offline-google-analytics *must* be imported and initialized before\n// sw-toolbox, because its 'fetch' event handler needs to run first.\nimportScripts('/sw-offline-google-analytics/offline-google-analytics-import.js');\ngoog.offlineGoogleAnalytics.initialize();\n\n// Use sw-toolbox\nimportScripts('/sw-toolbox/sw-toolbox.js'); /* global toolbox */\ntoolbox.options.debug = false;\n\nimportScripts('/js/sw-assets-precache.js'); /* global ASSETS */\n\nconst VERSION = '24';\nconst PREFIX = 'gulliver';\nconst CACHE_NAME = `${PREFIX}-v${VERSION}`;\nconst PWA_OPTION = {\n  cache: {\n    name: `PWA-${CACHE_NAME}`,\n    maxAgeSeconds: 60 * 60 * 12,\n    queryOptions: {\n      ignoreSearch: true\n    }\n  }\n};\nconst PWA_LIST_OPTION = {\n  cache: {\n    name: `LIST-${CACHE_NAME}`,\n    maxAgeSeconds: 60 * 60 * 6\n  }\n};\n\n// URL to return in place of the \"offline dino\" when client is\n// offline and requests a URL that's not in the cache.\nconst OFFLINE_URL = '/.app/offline';\nconst SHELL_URL = '/.app/shell';\n\nconst OFFLINE = [\n  OFFLINE_URL,\n  SHELL_URL,\n  '/?cacheOnly=true',\n  '/favicons/android-chrome-72x72.png',\n  '/manifest.json',\n  '/img/GitHub-Mark-Light-24px.png',\n  '/img/GitHub-Mark-Light-48px.png',\n  '/img/lighthouse-18.png',\n  '/img/lighthouse-36.png',\n  '/messaging-config.json'\n];\n\ntoolbox.precache(OFFLINE.concat(ASSETS));\ntoolbox.options.cache.name = CACHE_NAME;\n\n/**\n * Utility method to retrieve a url from the `toolbox.options.cache.name` cache\n *\n * @param {*} url url to be requested from the cache.\n */\nconst getFromCache = url => {\n  return caches.open(toolbox.options.cache.name)\n    .then(cache => cache.match(url));\n};\n\n/**\n * A sw-toolbox handler that tries to serve content using networkFirst, and if\n * it fails, returns a custom offline page.\n */\nconst gulliverHandler = (request, values, options) => {\n  return toolbox.fastest(request, values, options)\n    .catch(_ => {\n      // networkFirst failed (no network and not in cache)\n      getFromCache(OFFLINE_URL).then(response => {\n        return response || new Response('', {\n          status: 500,\n          statusText: 'Offline Page Missing'\n        });\n      });\n    });\n};\n\nconst getContentOnlyUrl = url => {\n  const u = new URL(url);\n  u.searchParams.append('contentOnly', 'true');\n  return u.toString();\n};\n\ntoolbox.router.default = (request, values, options) => {\n  if (request.mode === 'navigate') {\n    // Launch and early request to the content URL that will be loaded from the shell.\n    // Since the response has a short timeout, the browser will re-use the request.\n    toolbox.cacheFirst(new Request(getContentOnlyUrl(request.url)), values, options);\n\n    // Replace the request with the App Shell.\n    return getFromCache(SHELL_URL)\n      .then(response => response || gulliverHandler(request, values, options));\n  }\n  return gulliverHandler(request, values, options);\n};\n\ntoolbox.router.get(/\\/pwas\\/\\d+/, toolbox.router.default, PWA_OPTION);\n\ntoolbox.router.get('/pwas/score', toolbox.router.default, PWA_LIST_OPTION);\ntoolbox.router.get('/pwas/newest', toolbox.router.default, PWA_LIST_OPTION);\n\ntoolbox.router.get('/', (request, values) => {\n  // Replace requests to start_url with the lastest version of the root page.\n  // TODO Make more generic: strip utm_* parameters from *every* request.\n  // TODO Pass through credentials (e.g. cookies) and other request metadata, see\n  // https://github.com/ithinkihaveacat/sw-proxy/blob/master/http-proxy.ts#L249.\n  if (request.url.endsWith('/?utm_source=homescreen')) {\n    request = new Request('/');\n  }\n  return toolbox.router.default(request, values, PWA_LIST_OPTION);\n});\n\ntoolbox.router.get(/.*\\.(js|png|svg|jpg|css)$/, (request, values, options) => {\n  return toolbox.cacheFirst(request, values, options);\n});\n\n// API request bypass the Shell\ntoolbox.router.get(/\\/api\\/.*/, (request, values, options) => {\n  return toolbox.networkFirst(request, values, options);\n});\n\n// Claim all clients and delete old caches that are no longer needed.\nself.addEventListener('activate', event => {\n  self.clients.claim();\n  event.waitUntil(\n    caches.keys().then(cacheNames =>\n      Promise.all(\n        cacheNames.filter(cacheName => cacheName !== CACHE_NAME &&\n            cacheName !== PWA_OPTION.name &&\n            cacheName !== PWA_LIST_OPTION.name)\n          .map(cacheName => caches.delete(cacheName))\n      )\n    )\n  );\n});\n\n// Make sure the SW the page we register() is the service we use.\nself.addEventListener('install', () => self.skipWaiting());\n"
  },
  {
    "path": "rollup-config/gulliver.js",
    "content": "import babel from 'rollup-plugin-babel';\nimport uglify from 'rollup-plugin-uglify';\nimport nodeResolve from 'rollup-plugin-node-resolve';\nimport commonsjs from 'rollup-plugin-commonjs';\n\nexport default {\n  entry: './public/js/gulliver.es6.js',\n  plugins: [\n    babel({exclude: 'node_modules/**'}),\n    uglify(),\n    nodeResolve(),\n    commonsjs()\n  ],\n  // Quiet warning: https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined\n  context: 'window',\n  targets: [\n    {\n      dest: './public/js/gulliver.js',\n      // Fixes 'navigator' not defined when using Firebase and strict mode:\n      // http://stackoverflow.com/questions/31221357/webpack-firebase-disable-parsing-of-firebase\n      useStrict: false,\n      format: 'iife',\n      sourceMap: true\n    }\n  ]\n};\n"
  },
  {
    "path": "rollup-config/lighthouse-chart.js",
    "content": "import babel from 'rollup-plugin-babel';\nimport uglify from 'rollup-plugin-uglify';\nimport nodeResolve from 'rollup-plugin-node-resolve';\nimport commonsjs from 'rollup-plugin-commonjs';\n\nexport default {\n  entry: './public/js/lighthouse-chart.es6.js',\n  plugins: [\n    babel({exclude: 'node_modules/**'}),\n    uglify(),\n    nodeResolve(),\n    commonsjs()\n  ],\n  // Quiet warning: https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined\n  context: 'window',\n  targets: [\n    {\n      dest: './public/js/lighthouse-chart.js',\n      // Fixes 'navigator' not defined when using Firebase and strict mode:\n      // http://stackoverflow.com/questions/31221357/webpack-firebase-disable-parsing-of-firebase\n      useStrict: false,\n      format: 'iife',\n      sourceMap: true\n    }\n  ]\n};\n"
  },
  {
    "path": "rollup-config/pwa-form.js",
    "content": "import babel from 'rollup-plugin-babel';\nimport uglify from 'rollup-plugin-uglify';\nimport nodeResolve from 'rollup-plugin-node-resolve';\nimport commonsjs from 'rollup-plugin-commonjs';\n\nexport default {\n  entry: './public/js/pwa-form.es6.js',\n  plugins: [\n    babel({exclude: 'node_modules/**'}),\n    uglify(),\n    nodeResolve(),\n    commonsjs()\n  ],\n  // Quiet warning: https://github.com/rollup/rollup/wiki/Troubleshooting#this-is-undefined\n  context: 'window',\n  targets: [\n    {\n      dest: './public/js/pwa-form.js',\n      // Fixes 'navigator' not defined when using Firebase and strict mode:\n      // http://stackoverflow.com/questions/31221357/webpack-firebase-disable-parsing-of-firebase\n      useStrict: false,\n      format: 'iife',\n      sourceMap: true\n    }\n  ]\n};\n"
  },
  {
    "path": "test/app/controllers/api/favorite-pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nconst controllerApi = require('../../../../controllers/api');\nconst libFavoritePwa = require('../../../../lib/favorite-pwa');\nconst verifyIdToken = require('../../../../lib/verify-id-token');\n\nconst express = require('express');\nconst app = express();\nconst request = require('supertest');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nlet assert = require('chai').assert;\n\ndescribe('controllers.api.favorite-pwa', () => {\n  before(done => {\n    app.use(controllerApi);\n    done();\n  });\n\n  describe('GET /api/favorite-pwa/', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    const testGoogleLogin = {};\n    testGoogleLogin.getPayload = () => {\n      return {sub: '1234567890'};\n    };\n\n    it('respond with 401 if missing user idToken', done => {\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa')\n        .expect('Content-Type', /json/)\n        .expect(401).should.be.fulfilled.then(res => {\n          assert.equal(res.body, '401 Unauthorized');\n          done();\n        });\n    });\n\n    it('respond with 200 if Favorite PWA exist', done => {\n      simpleMock.mock(libFavoritePwa, 'findByUserId').resolveWith('list of favorite pwas');\n      simpleMock.mock(verifyIdToken, 'verifyIdToken').resolveWith(testGoogleLogin);\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa?idToken=1234567890').set('Authorization', 'ID_TOKEN')\n        .expect('Content-Type', /json/)\n        .expect(200).should.be.fulfilled.then(res => {\n          assert.equal(res.body, 'list of favorite pwas');\n          assert.equal(libFavoritePwa.findByUserId.callCount, 1);\n          assert.equal(libFavoritePwa.findByUserId.lastCall.arg,\n            '01b307acba4f54f55aafc33bb06bbbf6ca803e9a');\n          done();\n        });\n    });\n\n    it('respond with 404 if Favorite PWA does not exist', done => {\n      simpleMock.mock(libFavoritePwa, 'findByUserId').resolveWith(null);\n      simpleMock.mock(verifyIdToken, 'verifyIdToken').resolveWith(testGoogleLogin);\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa?idToken=1234567890').set('Authorization', 'ID_TOKEN')\n        .expect('Content-Type', /json/)\n        .expect(404).should.be.fulfilled.then(res => {\n          assert.equal(res.body, 'not found');\n          assert.equal(libFavoritePwa.findByUserId.callCount, 1);\n          assert.equal(libFavoritePwa.findByUserId.lastCall.arg,\n            '01b307acba4f54f55aafc33bb06bbbf6ca803e9a');\n          done();\n        });\n    });\n  });\n\n  describe('GET /api/favorite-pwa/:pwaId', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    const testGoogleLogin = {};\n    testGoogleLogin.getPayload = () => {\n      return {sub: '1234567890'};\n    };\n\n    it('respond with 401 if missing user idToken', done => {\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa/1234567')\n        .expect('Content-Type', /json/)\n        .expect(401).should.be.fulfilled.then(res => {\n          assert.equal(res.body, '401 Unauthorized');\n          done();\n        });\n    });\n\n    it('respond with 200 if findFavoritePwa exist', done => {\n      simpleMock.mock(libFavoritePwa, 'findFavoritePwa').resolveWith('list of favorite pwas');\n      simpleMock.mock(verifyIdToken, 'verifyIdToken').resolveWith(testGoogleLogin);\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa/1234567').set('Authorization', 'ID_TOKEN')\n        .expect('Content-Type', /json/)\n        .expect(200).should.be.fulfilled.then(res => {\n          assert.equal(res.body, 'list of favorite pwas');\n          assert.equal(libFavoritePwa.findFavoritePwa.callCount, 1);\n          assert.equal(libFavoritePwa.findFavoritePwa.lastCall.args[0],\n            '1234567');\n          assert.equal(libFavoritePwa.findFavoritePwa.lastCall.args[1],\n            '01b307acba4f54f55aafc33bb06bbbf6ca803e9a');\n          done();\n        });\n    });\n\n    it('respond with 404 if findFavoritePwa does not exist', done => {\n      simpleMock.mock(libFavoritePwa, 'findFavoritePwa').resolveWith(null);\n      simpleMock.mock(verifyIdToken, 'verifyIdToken').resolveWith(testGoogleLogin);\n      // /api/ is part of the router, we need to start from /favorite-pwa/\n      request(app)\n        .get('/favorite-pwa/1234567').set('Authorization', 'ID_TOKEN')\n        .expect('Content-Type', /json/)\n        .expect(404).should.be.fulfilled.then(res => {\n          assert.equal(res.body, 'not found');\n          assert.equal(libFavoritePwa.findFavoritePwa.callCount, 1);\n          assert.equal(libFavoritePwa.findFavoritePwa.lastCall.args[0],\n            '1234567');\n          assert.equal(libFavoritePwa.findFavoritePwa.lastCall.args[1],\n            '01b307acba4f54f55aafc33bb06bbbf6ca803e9a');\n          done();\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/controllers/api/lighthouse.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nconst controllerApi = require('../../../../controllers/api');\nconst lighthouseLib = require('../../../../lib/lighthouse');\n\nconst express = require('express');\nconst app = express();\nconst request = require('supertest');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nlet assert = require('chai').assert;\n\ndescribe('controllers.api.lighthouse', () => {\n  before(done => {\n    app.use(controllerApi);\n    done();\n  });\n\n  describe('GET /api/lighthouse-graph/', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('respond with 200 if PWA exist', done => {\n      simpleMock.mock(lighthouseLib, 'getLighthouseGraphByPwaId').resolveWith('mocked graph data');\n      // /api/ is part of the router, we need to start from /lighthouse-graph/\n      request(app)\n        .get('/lighthouse/graph/1234567')\n        .expect('Content-Type', /json/)\n        .expect(200).should.be.fulfilled.then(res => {\n          assert.equal(res.body, 'mocked graph data');\n          assert.equal(lighthouseLib.getLighthouseGraphByPwaId.callCount, 1);\n          assert.equal(lighthouseLib.getLighthouseGraphByPwaId.lastCall.arg, 1234567);\n          done();\n        });\n    });\n\n    it('respond with 404 if PWA does not exist', done => {\n      simpleMock.mock(lighthouseLib, 'getLighthouseGraphByPwaId').resolveWith(null);\n      // /api/ is part of the router, we need to start from /lighthouse-graph/\n      request(app)\n        .get('/lighthouse/graph/123')\n        .expect('Content-Type', /json/)\n        .expect(400).should.be.rejected.then(res => {\n          assert.equal(res.body, undefined);\n          assert.equal(lighthouseLib.getLighthouseGraphByPwaId.callCount, 1);\n          assert.equal(lighthouseLib.getLighthouseGraphByPwaId.lastCall.arg, '123');\n          done();\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/controllers/api/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nconst controllerApi = require('../../../../controllers/api');\nconst libPwa = require('../../../../lib/pwa');\nconst testPwa = require('../../models/pwa');\nconst config = require('../../../../config/config');\nconst apiKeyArray = config.get('API_TOKENS');\n\nconst express = require('express');\nconst app = express();\nconst request = require('supertest');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nlet assert = require('chai').assert;\n\nconst MANIFEST_URL = 'https://pwa-directory.appspot.com/manifest.json';\n/* eslint-disable camelcase */\nconst MANIFEST_DATA = {\n  name: 'PWA Directory',\n  short_name: 'PwaDirectory',\n  start_url: '/?utm_source=homescreen'\n};\n\ndescribe('controllers.api.pwa', () => {\n  before(done => {\n    app.use(controllerApi);\n    done();\n  });\n\n  describe('GET /api/pwa', () => {\n    const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n    pwa.id = '789';\n    const result = {};\n    result.pwas = [pwa];\n\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('respond with 200 and json', done => {\n      simpleMock.mock(libPwa, 'list').resolveWith(Promise.resolve(result));\n      // /api/ is part of the router, we need to start from /pwa/\n      request(app)\n        .get('/pwa?key=' + apiKeyArray[0])\n        .expect(200)\n        .expect('Content-Type', /json/).should.be.fulfilled.then(_ => {\n          assert.equal(libPwa.list.callCount, 1);\n          done();\n        });\n    });\n\n    it('respond with 200 and csv', done => {\n      simpleMock.mock(libPwa, 'list').resolveWith(Promise.resolve(result));\n      // /api/ is part of the router, we need to start from /pwa/\n      request(app)\n        .get('/pwa?format=csv&key=' + apiKeyArray[0])\n        .expect(200)\n        .expect('Content-Type', 'text/csv; charset=utf-8').should.be.fulfilled.then(_ => {\n          assert.equal(libPwa.list.callCount, 1);\n          done();\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/controllers/cache.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nconst controllersCache = require('../../../controllers/cache');\nconst libCache = require('../../../lib/data-cache');\n\nconst express = require('express');\nconst app = express();\nconst request = require('supertest');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nconst assert = require('chai').assert;\n\ndescribe('controllers.cache', () => {\n  before(done => {\n    app.use('/',\n      controllersCache,\n      (req, res) => {\n        res.send('<html>PageRendered</html>');\n      }\n    );\n    done();\n  });\n\n  describe('GET /', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('Page from cache', done => {\n      simpleMock.mock(libCache, 'get').resolveWith('<html>PageFromCache</html>');\n      simpleMock.mock(libCache, 'set').resolveWith();\n      request(app)\n        .get('/')\n        .expect(200).should.be.fulfilled.then(res => {\n          assert.equal(libCache.get.callCount, 1);\n          assert.equal(res.text, '<html>PageFromCache</html>');\n          done();\n        });\n    });\n\n    it('Not in cache, rendered directly', done => {\n      simpleMock.mock(libCache, 'get').rejectWith('Not in cache').resolveWith('/');\n      simpleMock.mock(libCache, 'set').resolveWith();\n      simpleMock.mock(libCache, 'storeCachedUrls').resolveWith();\n      request(app)\n        .get('/')\n        .expect(200).should.be.fulfilled.then(res => {\n          assert.equal(res.text, '<html>PageRendered</html>');\n          assert.equal(libCache.get.callCount, 1);\n          assert.equal(libCache.get.calls[0].args[0], '/');\n          assert.equal(libCache.set.callCount, 1);\n          assert.equal(libCache.set.calls[0].args[0], '/');\n          assert.equal(libCache.set.calls[0].args[1], '<html>PageRendered</html>');\n          assert.equal(libCache.storeCachedUrls.callCount, 1);\n          assert.equal(libCache.storeCachedUrls.calls[0].args[0], '/');\n          done();\n        })\n        .catch(err => {\n          console.log(err);\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/controllers/tasks.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nconst controllerTasks = require('../../../controllers/tasks');\nconst tasksLib = require('../../../lib/tasks');\nconst pwaLib = require('../../../lib/pwa');\nconst Pwa = require('../../../models/pwa');\n\nconst express = require('express');\nconst app = express();\nconst request = require('supertest');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nconst assert = require('chai').assert;\n\nconst APP_ENGINE_CRON = 'X-Appengine-Cron';\nconst MANIFEST_URL = 'https://pwa-directory.appspot.com/manifest.json';\n\ndescribe('controllers.tasks', () => {\n  let listPwas = {};\n  before(done => {\n    app.use(controllerTasks);\n    let pwa1 = new Pwa(MANIFEST_URL, null);\n    pwa1.id = 123456789;\n    let pwa2 = new Pwa(MANIFEST_URL, null);\n    pwa2.id = 234567890;\n    pwa2.lighthouseScore = 99;\n    listPwas.pwas = [pwa1, pwa2];\n    done();\n  });\n\n  describe('GET /tasks/cron', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('respond with 403 forbidden when X-Appengine-Cron not present', done => {\n      request(app)\n        .get('/cron')\n        .expect(403, done);\n    });\n  });\n\n  describe('GET /tasks/updateunscored', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('respond with 403 forbidden when X-Appengine-Cron not present', done => {\n      request(app)\n        .get('/updateunscored')\n        .expect(403, done);\n    });\n\n    it('respond with 200 when X-Appengine-Cron is present', done => {\n      simpleMock.mock(tasksLib, 'push').resolveWith(null);\n      simpleMock.mock(pwaLib, 'list').resolveWith(listPwas);\n      request(app)\n        .get('/updateunscored')\n        .set(APP_ENGINE_CRON, true)\n        .expect(200).should.be.fulfilled.then(_ => {\n          assert.equal(pwaLib.list.callCount, 1);\n          assert.equal(tasksLib.push.callCount, 1);\n          done();\n        });\n    });\n  });\n\n  describe('GET /tasks/execute', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('respond with 403 forbidden when X-Appengine-Cron not present', done => {\n      request(app)\n        .get('/execute')\n        .expect(403, done);\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/asset-hashing.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it beforeEach afterEach*/\n'use strict';\n\nconst assert = require('chai').assert;\nconst simple = require('simple-mock');\n\nconst assetHashing = require('../../../lib/asset-hashing');\n\ndescribe('ChecksumProvider', () => {\n  it('calculates checksum', () => {\n    const checksumProvider = new assetHashing.ChecksumProvider(__dirname);\n    assert.equal(checksumProvider.get('asset-hashing.js').length, assetHashing.CHECKSUM_LENGTH);\n  });\n});\n\ndescribe('AssetChecksum', () => {\n  let checksumProvider = new assetHashing.ChecksumProvider();\n  let asset;\n\n  beforeEach(() => {\n    simple.mock(checksumProvider, 'get', () => '1234567890');\n    asset = new assetHashing.AssetChecksum(checksumProvider);\n  });\n\n  describe('encode', () => {\n    it('adds checksum to file name', () => {\n      assert.equal(asset.encode('public/style.css'), 'public/style.1234567890.css');\n    });\n    it('ignores dirs', () => {\n      assert.equal(asset.encode('public/style'), 'public/style');\n    });\n    it('ignores empty string', () => {\n      assert.equal(asset.encode(''), '');\n    });\n    it('ignores null', () => {\n      assert.equal(asset.encode(null), null);\n    });\n    it('caches results', () => {\n      asset.encode('public/style.css');\n      asset.encode('public/style.css');\n      assert.equal(checksumProvider.get.callCount, 1);\n    });\n  });\n\n  describe('decode', () => {\n    it('ignores dirs', () => {\n      assert.equal(asset.decode('public/style'), 'public/style');\n    });\n    it('ignores empty string', () => {\n      assert.equal(asset.decode(''), '');\n    });\n    it('ignores null', () => {\n      assert.equal(asset.decode(null), null);\n    });\n    it('ignores non checksums', () => {\n      assert.equal(asset.decode('style.12345/7890.css'), 'style.12345/7890.css');\n    });\n    it('removes checksum from file name', () => {\n      assert.equal(asset.decode('public/style.1234567890.css'), 'public/style.css');\n    });\n    it('only checksums with length of 10', () => {\n      assert.equal(asset.decode('public/style.123456789.css'), 'public/style.123456789.css');\n      assert.equal(asset.decode('public/style.12345678900.css'), 'public/style.12345678900.css');\n    });\n  });\n\n  afterEach(() => {\n    simple.restore();\n  });\n});\n\n"
  },
  {
    "path": "test/app/lib/color.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n'use strict';\n\nconst assert = require('chai').assert;\nconst color = require('../../../lib/color');\n\ndescribe('color.js', () => {\n  describe('contrastRatio', () => {\n    it('Calculates correct ratio for #000000', () => {\n      const ratio = color.contrastRatio('#000000');\n      assert.equal(ratio, 21);\n    });\n\n    it('Calculates correct ratio for #000000 / #FFFFFF', () => {\n      const ratio = color.contrastRatio('#000000', '#FFFFFF');\n      assert.equal(ratio, 21);\n    });\n\n    it('Calculates correct ratio for #FFFFFF / #000000', () => {\n      const ratio = color.contrastRatio('#FFFFFF', '#000000');\n      assert.equal(ratio, 21);\n    });\n\n    it('Calculates correct ratio for #FFFFFF / #FFFFFF', () => {\n      const ratio = color.contrastRatio('#FFFFFF', '#FFFFFF');\n      assert.equal(ratio, 1);\n    });\n\n    it('Calculates correct ratio for #FFFFFF / transparent', () => {\n      const ratio = color.contrastRatio('#FFFFFF', 'transparent');\n      assert.equal(ratio, 1);\n    });\n  });\n\n  describe('bestContrastRatio', () => {\n    it('Selects best contrast between #000000 and #FFFFFF agains #000000', () => {\n      const bestContrast = color.bestContrastRatio('#000000', '#FFFFFF', '#000000');\n      assert.equal(bestContrast, '#FFFFFF');\n    });\n\n    it('Selects best contrast between #000000 and #FFFFFF agains black', () => {\n      const bestContrast = color.bestContrastRatio('#000000', '#FFFFFF', 'black');\n      assert.equal(bestContrast, '#FFFFFF');\n    });\n  });\n\n  describe('relativeLuminance', () => {\n    it('Calculates correct luminance for #FFFFFF', () => {\n      const luminance = color.relativeLuminance('#FFFFFF');\n      assert.equal(luminance, 1);\n    });\n\n    it('Calculates correct luminance for #000000', () => {\n      const luminance = color.relativeLuminance('#000000');\n      assert.equal(luminance, 0);\n    });\n\n    it('Calculates correct luminance for \"#000000 \"', () => {\n      const luminance = color.relativeLuminance('#000000 ');\n      assert.equal(luminance, 0);\n    });\n\n    it('Calculates correct luminance for \"black\"', () => {\n      const luminance = color.relativeLuminance('black');\n      assert.equal(luminance, 0);\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/data-fetcher.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it afterEach*/\n'use strict';\n\nlet dataFetcher = require('../../../lib/data-fetcher');\nconst simpleMock = require('simple-mock');\nlet chai = require('chai');\nlet chaiAsPromised = require('chai-as-promised');\nconst assert = require('chai').assert;\nchai.use(chaiAsPromised);\nchai.should();\n\nconst LIGHTHOUSE_JSON_EXAMPLE = './test/app/lib/lighthouse-example.json';\n\ndescribe('lib.data-fetcher', () => {\n  it('fetchMetadataDescription(null) should fail', () => {\n    return dataFetcher.fetchMetadataDescription(null).should.be.rejectedWith(Error);\n  });\n\n  it('fetchMetadataDescription(https://www.google.com) should work', () => {\n    return dataFetcher.fetchMetadataDescription('https://www.google.com').should.be.fulfilled;\n  });\n\n  it('readfile(LIGHTHOUSE_JSON_EXAMPLE) should work', () => {\n    return dataFetcher.readFile(LIGHTHOUSE_JSON_EXAMPLE).should.be.fulfilled;\n  });\n\n  describe('#_firebaseOptions', () => {\n    it('should call with GET method', () => {\n      const options = dataFetcher._firebaseOptions();\n      assert.equal(options.method, 'GET');\n      assert(options.headers.Authorization, 'Should contain Authorization header');\n    });\n\n    it('should call with POST method when payload exists', () => {\n      const options = dataFetcher._firebaseOptions({});\n      assert.equal(options.method, 'POST');\n      assert(options.headers.Authorization, 'Should contain Authorization header');\n      assert.equal(options.headers['content-type'], 'application/json', 'Correct content-type');\n    });\n  });\n\n  describe('#_handleFirebaseResponse', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('should succeed when code is 200', () => {\n      const response = {};\n      simpleMock.mock(response, 'status', 200);\n      simpleMock.mock(response, 'json').resolveWith({});\n\n      return dataFetcher._handleFirebaseResponse(response).should.be.fulfilled\n        .then(() => {\n          assert(response.json.called);\n        });\n    });\n\n    it('should reject when code is not 200', () => {\n      const response = {};\n      simpleMock.mock(response, 'status', 402);\n      simpleMock.mock(response, 'text').resolveWith({});\n\n      return dataFetcher._handleFirebaseResponse(response).should.be.rejected\n        .then(() => {\n          assert(response.text.called);\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/favorite-pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before beforeEach */\n'use strict';\n\nconst assert = require('assert');\nconst config = require('../../../config/config');\n\nconst datastore = require('@google-cloud/datastore');\nconst ds = datastore({\n  projectId: config.get('GCLOUD_PROJECT')\n});\n\nconst FavoritePwa = require('../../../models/favorite-pwa');\nconst libFavoritePwa = require('../../../lib/favorite-pwa');\n\nconst ENTITY_NAME = 'FAVORITE-PWA';\nconst TEST_FAV_PWA = new FavoritePwa(123456789, 987654321);\n\ndescribe('lib.favorite-pwa', () => {\n  const skipTests = process.env.TRAVIS;\n  // Skip tests if Running in CI\n  before(function() {\n    this.timeout(3000);\n    if (skipTests) {\n      this.skip();\n      return;\n    }\n\n    // Deletes all entities on the 'test' namespace before each test.\n    return new Promise((resolve, reject) => {\n      const q = ds.createQuery(ENTITY_NAME).filter('pwaId', '=', parseInt(TEST_FAV_PWA.pwaId, 10));\n      ds.runQuery(q, (err, entities) => {\n        if (err) {\n          return reject(err);\n        }\n\n        const keys = entities.map(entity => {\n          return entity.key;\n        });\n\n        // Delete counts for 'test'.\n        keys[keys.length] = ds.key(['counts', ENTITY_NAME]);\n        ds.delete(keys, err => {\n          return reject(err);\n        });\n\n        return resolve();\n      });\n    });\n  });\n\n  describe('#save and find', () => {\n    beforeEach(function() {\n      if (skipTests) {\n        this.skip();\n        return;\n      }\n    });\n\n    let savedFavoritePwa;\n    before(() => {\n      if (skipTests) {\n        return;\n      }\n      return libFavoritePwa.save(TEST_FAV_PWA)\n        .then(saved => {\n          savedFavoritePwa = saved;\n        });\n    });\n\n    it('save', () => {\n      assert.equal(savedFavoritePwa.pwaId, TEST_FAV_PWA.pwaId);\n      assert.equal(savedFavoritePwa.userId, TEST_FAV_PWA.userId);\n    });\n\n    it('findByUserId', () => {\n      return libFavoritePwa.findByUserId(TEST_FAV_PWA.userId)\n        .then(foundFavoritePwas => {\n          assert.equal(foundFavoritePwas[0].pwaId, TEST_FAV_PWA.pwaId);\n          assert.equal(foundFavoritePwas[0].userId, TEST_FAV_PWA.userId);\n        });\n    });\n\n    it('findFavoritePwa', () => {\n      return libFavoritePwa.findFavoritePwa(TEST_FAV_PWA.pwaId, TEST_FAV_PWA.userId)\n        .then(foundFavoritePwa => {\n          assert.equal(foundFavoritePwa.pwaId, TEST_FAV_PWA.pwaId);\n          assert.equal(foundFavoritePwa.userId, TEST_FAV_PWA.userId);\n        });\n    });\n  });\n\n  describe('#delete', () => {\n    beforeEach(function() {\n      if (skipTests) {\n        this.skip();\n        return;\n      }\n    });\n\n    it('delete', () => {\n      return libFavoritePwa.save(TEST_FAV_PWA)\n        .then(saved => {\n          return libFavoritePwa.delete(saved.id).should.be.fulfilled;\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/images.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it afterEach before */\n'use strict';\n\nconst libImages = require('../../../lib/images');\nconst dataFetcher = require('../../../lib/data-fetcher');\nconst Manifest = require('../../../models/manifest');\n\nconst httpMocks = require('node-mocks-http');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nconst assert = require('chai').assert;\n\nconst MANIFEST_URL = 'https://mobile.twitter.com/manifest.json';\nconst MANIFEST_DATA = './test/app/manifests/inline-image-large-content.json';\n\ndescribe('lib.images', () => {\n  let manifest;\n  before(done => {\n    dataFetcher.readFile(MANIFEST_DATA)\n      .then(jsonString => {\n        manifest = new Manifest(MANIFEST_URL, JSON.parse(jsonString));\n        done();\n      });\n  });\n  afterEach(() => {\n    simpleMock.restore();\n  });\n\n  it('fetchAndSave fail fetch for HTTP error', () => {\n    const response = {};\n    response.status = 400;\n    simpleMock.mock(dataFetcher, 'fetchWithUA').resolveWith(response);\n    simpleMock.mock(libImages, 'saveImage').resolveWith(null);\n    return libImages.fetchAndSave('http://www.test.com', null).should.be.rejectedWith(\n      'Bad Response (400) loading image: undefined');\n  });\n\n  it('fetchAndSave fail for unsoported protocol (ftp:)', () => {\n    const response = {};\n    response.status = 400;\n    simpleMock.mock(dataFetcher, 'fetchWithUA').resolveWith(response);\n    simpleMock.mock(libImages, 'saveImage').resolveWith(null);\n    return libImages.fetchAndSave('ftp://www.test.com', null).should.be.rejectedWith(\n      'Unsupported Protocol: ftp:');\n  });\n\n  it('fetchAndSave works with http url', () => {\n    const headers = {};\n    simpleMock.mock(headers, 'get').returnWith('image/jpeg');\n    const response = httpMocks.createResponse();\n    response.headers = headers;\n    response.status = 200;\n    response.body = '';\n    simpleMock.mock(dataFetcher, 'fetchWithUA').resolveWith(response);\n    simpleMock.mock(libImages, 'saveImage').resolveWith('http://url.for.newimage.in.bucket.com');\n    return libImages.fetchAndSave('http://www.test.com', 'destFile').should.be.fulfilled.then(_ => {\n      assert.equal(libImages.saveImage.callCount, 3);\n    });\n  });\n\n  it('fetchAndSave works with https url', () => {\n    const response = httpMocks.createResponse();\n    const headers = {};\n    simpleMock.mock(headers, 'get').returnWith('image/jpeg');\n    response.headers = headers;\n    response.status = 200;\n    response.body = '';\n    simpleMock.mock(dataFetcher, 'fetchWithUA').resolveWith(response);\n    simpleMock.mock(libImages, 'saveImage').resolveWith('http://url.for.newimage.in.bucket.com');\n    return libImages.fetchAndSave('https://www.test.com', 'destFile').should.be.fulfilled.then(_ => {\n      assert.equal(libImages.saveImage.callCount, 3);\n    });\n  });\n\n  it('dataUriAndSave data uri', () => {\n    const bestIconUrl = manifest.getBestIconUrl();\n    simpleMock.mock(libImages, 'saveImage').resolveWith('http://url.for.newimage.in.bucket.com');\n    return libImages.dataUriAndSave(bestIconUrl).should.be.fulfilled.then(_ => {\n      assert.equal(libImages.saveImage.callCount, 3);\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/lighthouse-example.json",
    "content": "[\n  {\n  \"id\": \"5636139285217280-20180307\",\n  \"webPageUrlId\": \"5636139285217280\",\n  \"url\": \"https://pwa-directory.appspot.com/\",\n  \"date\": \"20180307\",\n  \"score\": 90,\n  \"pwaScore\": 90.91,\n  \"pwaWeight\": 1,\n  \"performanceScore\": 41.12,\n  \"performanceWeight\": 0,\n  \"accessibilityScore\": 54.55,\n  \"accessibilityWeight\": 0,\n  \"bestPracticesScore\": 100,\n  \"bestPracticesWeight\": 0,\n  \"rawData\": {\n    \"value\": \"{\\\"userAgent\\\":\\\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36\\\",\\\"lighthouseVersion\\\":\\\"2.9.1\\\",\\\"generatedTime\\\":\\\"2018-03-07T09:58:34.829Z\\\",\\\"initialUrl\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"url\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"runWarnings\\\":[],\\\"audits\\\":{\\\"is-on-https\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"is-on-https\\\",\\\"description\\\":\\\"Uses HTTPS\\\",\\\"helpText\\\":\\\"All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Insecure URLs:\\\"},\\\"items\\\":[]}},\\\"redirects-http\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"redirects-http\\\",\\\"description\\\":\\\"Redirects HTTP traffic to HTTPS\\\",\\\"helpText\\\":\\\"If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).\\\"},\\\"service-worker\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"service-worker\\\",\\\"description\\\":\\\"Registers a service worker\\\",\\\"helpText\\\":\\\"The service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).\\\"},\\\"works-offline\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"works-offline\\\",\\\"description\\\":\\\"Responds with a 200 when offline\\\",\\\"helpText\\\":\\\"If you're building a Progressive Web App, consider using a service worker so that your app can work offline. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).\\\"},\\\"viewport\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"viewport\\\",\\\"description\\\":\\\"Has a `<meta name=\\\\\\\"viewport\\\\\\\">` tag with `width` or `initial-scale`\\\",\\\"helpText\\\":\\\"Add a viewport meta tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).\\\"},\\\"without-javascript\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"without-javascript\\\",\\\"description\\\":\\\"Contains some content when JavaScript is not available\\\",\\\"helpText\\\":\\\"Your app should display some content when JavaScript is disabled, even if it's just a warning to the user that JavaScript is required to use the app. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).\\\"},\\\"first-meaningful-paint\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"1,040 ms\\\",\\\"rawValue\\\":1037.2,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"timestamps\\\":{\\\"navStart\\\":2152463602,\\\"fCP\\\":2153500775,\\\"fMP\\\":2153500778,\\\"onLoad\\\":2156528062,\\\"endOfTrace\\\":2161852046},\\\"timings\\\":{\\\"navStart\\\":0,\\\"fCP\\\":1037.173,\\\"fMP\\\":1037.176,\\\"onLoad\\\":4064.46,\\\"endOfTrace\\\":9388.444},\\\"fmpFellBack\\\":false}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"first-meaningful-paint\\\",\\\"description\\\":\\\"First meaningful paint\\\",\\\"helpText\\\":\\\"First meaningful paint measures when the primary content of a page is visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).\\\"},\\\"load-fast-enough-for-pwa\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"load-fast-enough-for-pwa\\\",\\\"description\\\":\\\"Page load is not fast enough on 3G\\\",\\\"helpText\\\":\\\"A fast page load over a 3G network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).\\\"},\\\"speed-index-metric\\\":{\\\"score\\\":99,\\\"displayValue\\\":\\\"1,142\\\",\\\"rawValue\\\":1142,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"timings\\\":{\\\"firstVisualChange\\\":1037,\\\"visuallyReady\\\":1037.3799999998882,\\\"visuallyComplete\\\":2898,\\\"perceptualSpeedIndex\\\":1141.835581201553},\\\"timestamps\\\":{\\\"firstVisualChange\\\":2153500602,\\\"visuallyReady\\\":2153500982,\\\"visuallyComplete\\\":2155361602,\\\"perceptualSpeedIndex\\\":2153605437.5812016},\\\"frames\\\":[{\\\"timestamp\\\":2152463.602,\\\"progress\\\":0},{\\\"timestamp\\\":2153500.982,\\\"progress\\\":90.31905946956357},{\\\"timestamp\\\":2153832.606,\\\"progress\\\":90.31905946956357},{\\\"timestamp\\\":2153849.478,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153870.388,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153883.379,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153921.808,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153932.306,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2153983.395,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2153999.242,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2154046.561,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2154066.125,\\\"progress\\\":95.22683766928108},{\\\"timestamp\\\":2154082.415,\\\"progress\\\":95.22683766928108},{\\\"timestamp\\\":2154118.314,\\\"progress\\\":95.23115222717236},{\\\"timestamp\\\":2154962.536,\\\"progress\\\":97.56635937215954},{\\\"timestamp\\\":2155362.197,\\\"progress\\\":100}]}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"speed-index-metric\\\",\\\"description\\\":\\\"Perceptual Speed Index\\\",\\\"helpText\\\":\\\"Speed Index shows how quickly the contents of a page are visibly populated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).\\\"},\\\"screenshot-thumbnails\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"true\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"screenshot-thumbnails\\\",\\\"description\\\":\\\"Screenshot Thumbnails\\\",\\\"helpText\\\":\\\"This is what the load of your site looked like.\\\",\\\"details\\\":{\\\"type\\\":\\\"filmstrip\\\",\\\"scale\\\":2898,\\\"items\\\":[{\\\"timing\\\":290,\\\"timestamp\\\":2152753402,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":580,\\\"timestamp\\\":2153043202,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":869,\\\"timestamp\\\":2153333002,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":1159,\\\"timestamp\\\":2153622802,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":1449,\\\"timestamp\\\":2153912602,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":1739,\\\"timestamp\\\":2154202402,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2029,\\\"timestamp\\\":2154492202,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2318,\\\"timestamp\\\":2154782002,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2608,\\\"timestamp\\\":2155071802,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2898,\\\"timestamp\\\":2155361602,\\\"data\\\":\\\"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\\\"}]}},\\\"estimated-input-latency\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"21 ms\\\",\\\"rawValue\\\":20.8,\\\"extendedInfo\\\":{\\\"value\\\":[{\\\"percentile\\\":0.5,\\\"time\\\":16},{\\\"percentile\\\":0.75,\\\"time\\\":16},{\\\"percentile\\\":0.9,\\\"time\\\":20.799407692307533},{\\\"percentile\\\":0.99,\\\"time\\\":90.00607999999784},{\\\"percentile\\\":1,\\\"time\\\":128.2549999999901}]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"estimated-input-latency\\\",\\\"description\\\":\\\"Estimated Input Latency\\\",\\\"helpText\\\":\\\"The score above is an estimate of how long your app takes to respond to user input, in milliseconds. There is a 90% probability that a user encounters this amount of latency, or less. 10% of the time a user can expect additional latency. If your latency is higher than 50 ms, users may perceive your app as laggy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).\\\"},\\\"errors-in-console\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"errors-in-console\\\",\\\"description\\\":\\\"No browser errors logged to the console\\\",\\\"helpText\\\":\\\"Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Description\\\"}],\\\"items\\\":[]}},\\\"time-to-first-byte\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"560 ms\\\",\\\"rawValue\\\":564.1660000001137,\\\"debugString\\\":\\\"\\\",\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":-35.83399999988626}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"time-to-first-byte\\\",\\\"description\\\":\\\"Keep server response times low (TTFB)\\\",\\\"helpText\\\":\\\"Time To First Byte identifies the time at which your server sends a response. [Learn more](https://developers.google.com/web/tools/chrome-devtools/network-performance/issues).\\\"},\\\"first-interactive\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"first-interactive\\\",\\\"description\\\":\\\"First Interactive (beta)\\\",\\\"helpText\\\":\\\"First Interactive marks the time at which the page is minimally interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).\\\"},\\\"consistently-interactive\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_TTI_CPU_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"consistently-interactive\\\",\\\"description\\\":\\\"Consistently Interactive (beta)\\\",\\\"helpText\\\":\\\"Consistently Interactive marks the time at which the page is fully interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).\\\"},\\\"user-timings\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"0\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"user-timings\\\",\\\"description\\\":\\\"User Timing marks and measures\\\",\\\"helpText\\\":\\\"Consider instrumenting your app with the User Timing API to create custom, real-world measurements of key user experiences. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Name\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Type\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time\\\"}],\\\"items\\\":[]}},\\\"critical-request-chains\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"0\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"chains\\\":{\\\"2047.1\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"startTime\\\":2152.467431,\\\"endTime\\\":2153.09461,\\\"responseReceivedTime\\\":2153.061376,\\\"transferSize\\\":6367},\\\"children\\\":{}},\\\"2047.2\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"startTime\\\":2153.127787,\\\"endTime\\\":2153.236907,\\\"responseReceivedTime\\\":2153.188976,\\\"transferSize\\\":4740},\\\"children\\\":{}},\\\"2047.3\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"startTime\\\":2153.128558,\\\"endTime\\\":2153.473274,\\\"responseReceivedTime\\\":2153.292108,\\\"transferSize\\\":20538},\\\"children\\\":{}}},\\\"longestChain\\\":{\\\"duration\\\":1005.8429999999134,\\\"length\\\":1,\\\"transferSize\\\":20538}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"critical-request-chains\\\",\\\"description\\\":\\\"Critical Request Chains\\\",\\\"helpText\\\":\\\"The Critical Request Chains below show you what resources are issued with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains).\\\",\\\"details\\\":{\\\"type\\\":\\\"criticalrequestchain\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View critical network waterfall:\\\"},\\\"chains\\\":{\\\"2047.1\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"startTime\\\":2152.467431,\\\"endTime\\\":2153.09461,\\\"responseReceivedTime\\\":2153.061376,\\\"transferSize\\\":6367},\\\"children\\\":{}},\\\"2047.2\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"startTime\\\":2153.127787,\\\"endTime\\\":2153.236907,\\\"responseReceivedTime\\\":2153.188976,\\\"transferSize\\\":4740},\\\"children\\\":{}},\\\"2047.3\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"startTime\\\":2153.128558,\\\"endTime\\\":2153.473274,\\\"responseReceivedTime\\\":2153.292108,\\\"transferSize\\\":20538},\\\"children\\\":{}}},\\\"longestChain\\\":{\\\"duration\\\":1005.8429999999134,\\\"length\\\":1,\\\"transferSize\\\":20538}}},\\\"redirects\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"0 ms\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"redirects\\\",\\\"description\\\":\\\"Avoids page redirects\\\",\\\"helpText\\\":\\\"Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/speed/docs/insights/AvoidRedirects).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Redirected URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time for Redirect\\\"}],\\\"items\\\":[]}},\\\"webapp-install-banner\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"warnings\\\":[],\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"webapp-install-banner\\\",\\\"description\\\":\\\"User can be prompted to Install the Web App\\\",\\\"helpText\\\":\\\"Browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).\\\"},\\\"splash-screen\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"splash-screen\\\",\\\"description\\\":\\\"Configured for a custom splash screen\\\",\\\"helpText\\\":\\\"A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).\\\"},\\\"themed-omnibox\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]},\\\"themeColor\\\":\\\"#7cc0ff\\\"}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"themed-omnibox\\\",\\\"description\\\":\\\"Address bar matches brand colors\\\",\\\"helpText\\\":\\\"The browser address bar can be themed to match your site. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).\\\"},\\\"manifest-short-name-length\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"manifest-short-name-length\\\",\\\"description\\\":\\\"Manifest's `short_name` won't be truncated when displayed on homescreen\\\",\\\"helpText\\\":\\\"Make your app's `short_name` fewer than 12 characters to ensure that it's not truncated on homescreens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/manifest-short_name-is-not-truncated).\\\"},\\\"content-width\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"content-width\\\",\\\"description\\\":\\\"Content is sized correctly for the viewport\\\",\\\"helpText\\\":\\\"If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).\\\"},\\\"image-aspect-ratio\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"image-aspect-ratio\\\",\\\"description\\\":\\\"Displays images with correct aspect ratio\\\",\\\"helpText\\\":\\\"Image display dimensions should match natural aspect ratio.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Aspect Ratio (Displayed)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Aspect Ratio (Actual)\\\"}],\\\"items\\\":[]}},\\\"deprecations\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"deprecations\\\",\\\"description\\\":\\\"Avoids deprecated APIs\\\",\\\"helpText\\\":\\\"Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Deprecation / Warning\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Line\\\"}],\\\"items\\\":[]}},\\\"mainthread-work-breakdown\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"1,050 ms\\\",\\\"rawValue\\\":1046.6150000002235,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"Evaluate Script\\\":303.1429999987595,\\\"DOM GC\\\":203.60300000058487,\\\"Layout\\\":169.78400000045076,\\\"Parse HTML\\\":65.91100000031292,\\\"Recalculate Style\\\":58.389999999664724,\\\"Paint\\\":54.391999998129904,\\\"Minor GC\\\":37.812999999616295,\\\"Composite Layers\\\":30.600000000093132,\\\"Run Microtasks\\\":30.269000000786036,\\\"Major GC\\\":30.259999999776483,\\\"Compile Script\\\":25.316000001039356,\\\"Update Layer Tree\\\":19.307000000029802,\\\"Parse Stylesheet\\\":10.522000000346452,\\\"XHR Ready State Change\\\":7.12000000057742,\\\"Image Decode\\\":0.1790000000037253,\\\"XHR Load\\\":0.006000000052154064}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"mainthread-work-breakdown\\\",\\\"description\\\":\\\"Main thread work breakdown\\\",\\\"helpText\\\":\\\"Consider reducing the time spent parsing, compiling and executing JS.You may find delivering smaller JS payloads helps with this.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Category\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Work\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time spent\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Evaluate Script\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"303 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Run Microtasks\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"XHR Ready State Change\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"XHR Load\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"DOM GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"204 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Minor GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"38 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Major GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Style & Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"170 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Style & Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Recalculate Style\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"58 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parsing HTML & CSS\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parse HTML\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"66 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parsing HTML & CSS\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parse Stylesheet\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Paint\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Paint\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"54 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compositing\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Composite Layers\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"31 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compositing\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Update Layer Tree\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"19 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Parsing & Compile\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compile Script\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Images\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Image Decode\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}]]}},\\\"bootup-time\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"400 ms\\\",\\\"rawValue\\\":398.2899999995716,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"https://www.google-analytics.com/analytics.js\\\":{\\\"Script Evaluation\\\":88.33199999993667,\\\"Script Parsing & Compile\\\":5.565000000409782},\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\":{\\\"Script Evaluation\\\":80.06300000008196,\\\"Script Parsing & Compile\\\":4.246999999973923,\\\"Style & Layout\\\":2.6689999997615814},\\\"https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.dLR0UQgpDEo.O/m=auth2/rt=j/sv=1/d=1/ed=1/am=AQE/rs=AGLTcCMdWrzahDTQIubih7dySWJqcBU_nw/cb=gapi.loaded_0\\\":{\\\"Script Evaluation\\\":73.78999999910593,\\\"Script Parsing & Compile\\\":3.5409999997355044,\\\"Style & Layout\\\":1.125},\\\"https://pwa-directory.appspot.com/\\\":{\\\"Parsing HTML & CSS\\\":56.77900000009686},\\\"https://accounts.google.com/o/oauth2/iframe\\\":{\\\"Script Evaluation\\\":29.386999999638647,\\\"Script Parsing & Compile\\\":0.12700000032782555},\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\":{\\\"Script Evaluation\\\":19.56899999966845,\\\"Script Parsing & Compile\\\":6.076000000350177,\\\"Style & Layout\\\":0.04199999989941716},\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\":{\\\"Script Evaluation\\\":6.666000000201166,\\\"Script Parsing & Compile\\\":5.13799999980256},\\\"https://accounts.google.com/o/oauth2/iframe#origin=https%3A%2F%2Fpwa-directory.appspot.com&rpcToken=401330920.1489836&clearCache=1\\\":{\\\"Parsing HTML & CSS\\\":7.239000000059605},\\\"https://accounts.google.com/o/oauth2/iframerpc?action=checkOrigin&origin=https%3A%2F%2Fpwa-directory.appspot.com&client_id=896499748108-ru4bhfh743clb3v3dc8bgf1v68umkvcu.apps.googleusercontent.com\\\":{\\\"Script Evaluation\\\":7.126000000629574},\\\"https://pwa-directory.appspot.com/sw.js\\\":{\\\"Parsing HTML & CSS\\\":0.8089999998919666}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"bootup-time\\\",\\\"description\\\":\\\"JavaScript boot-up time\\\",\\\"helpText\\\":\\\"Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Script Parsing & Compile\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"88 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"80 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.dLR0UQgpDEo.O/m=auth2/rt=j/sv=1/d=1/ed=1/am=AQE/rs=AGLTcCMdWrzahDTQIubih7dySWJqcBU_nw/cb=gapi.loaded_0\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"74 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://accounts.google.com/o/oauth2/iframe\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 ms\\\"}]]}},\\\"uses-rel-preload\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"0 ms\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-rel-preload\\\",\\\"description\\\":\\\"Preload key requests\\\",\\\"helpText\\\":\\\"Consider using <link rel=preload> to prioritize fetching late-discovered resources sooner [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"font-display\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"font-display\\\",\\\"description\\\":\\\"All text remains visible during webfont loads\\\",\\\"helpText\\\":\\\"Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading. [Learn more](https://developers.google.com/web/updates/2016/02/font-display).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Font URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Font download time\\\"}],\\\"items\\\":[]}},\\\"pwa-cross-browser\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-cross-browser\\\",\\\"description\\\":\\\"Site works cross-browser\\\",\\\"helpText\\\":\\\"To reach the most number of users, sites should work across every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).\\\"},\\\"pwa-page-transitions\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-page-transitions\\\",\\\"description\\\":\\\"Page transitions don't feel like they block on the network\\\",\\\"helpText\\\":\\\"Transitions should feel snappy as you tap around, even on a slow network, a key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).\\\"},\\\"pwa-each-page-has-url\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-each-page-has-url\\\",\\\"description\\\":\\\"Each page has a URL\\\",\\\"helpText\\\":\\\"Ensure individual pages are deep linkable via the URLs and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).\\\"},\\\"accesskeys\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"accesskeys\\\",\\\"description\\\":\\\"`[accesskey]` values are not unique\\\",\\\"helpText\\\":\\\"Access keys let users quickly focus a part of the page. For proper navigation, each access key must be unique. [Learn more](https://dequeuniversity.com/rules/axe/2.2/accesskeys?application=lighthouse).\\\"},\\\"aria-allowed-attr\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-allowed-attr\\\",\\\"description\\\":\\\"`[aria-*]` attributes do not match their roles\\\",\\\"helpText\\\":\\\"Each ARIA `role` supports a specific subset of `aria-*` attributes. Mismatching these invalidates the `aria-*` attributes. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-allowed-attr?application=lighthouse).\\\"},\\\"aria-required-attr\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-attr\\\",\\\"description\\\":\\\"`[role]`s do not have all required `[aria-*]` attributes\\\",\\\"helpText\\\":\\\"Some ARIA roles have required attributes that describe the state of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-attr?application=lighthouse).\\\"},\\\"aria-required-children\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-children\\\",\\\"description\\\":\\\"Elements with `[role]` that require specific children `[role]`s, are missing.\\\",\\\"helpText\\\":\\\"Some ARIA parent roles must contain specific child roles to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-children?application=lighthouse).\\\"},\\\"aria-required-parent\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-parent\\\",\\\"description\\\":\\\"`[role]`s are not contained by their required parent element\\\",\\\"helpText\\\":\\\"Some ARIA child roles must be contained by specific parent roles to properly perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-parent?application=lighthouse).\\\"},\\\"aria-roles\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-roles\\\",\\\"description\\\":\\\"`[role]` values are not valid\\\",\\\"helpText\\\":\\\"ARIA roles must have valid values in order to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-roles?application=lighthouse).\\\"},\\\"aria-valid-attr-value\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-valid-attr-value\\\",\\\"description\\\":\\\"`[aria-*]` attributes do not have valid values\\\",\\\"helpText\\\":\\\"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid values. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr-value?application=lighthouse).\\\"},\\\"aria-valid-attr\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-valid-attr\\\",\\\"description\\\":\\\"`[aria-*]` attributes are not valid or misspelled\\\",\\\"helpText\\\":\\\"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid names. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr?application=lighthouse).\\\"},\\\"audio-caption\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"audio-caption\\\",\\\"description\\\":\\\"`<audio>` elements are missing a `<track>` element with `[kind=\\\\\\\"captions\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"Captions make audio elements usable for deaf or hearing-impaired users, providing critical information such as who is talking, what they're saying, and other non-speech information. [Learn more](https://dequeuniversity.com/rules/axe/2.2/audio-caption?application=lighthouse).\\\"},\\\"button-name\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"button-name\\\",\\\"impact\\\":\\\"critical\\\",\\\"tags\\\":[\\\"cat.name-role-value\\\",\\\"wcag2a\\\",\\\"wcag412\\\",\\\"section508\\\",\\\"section508.22.a\\\"],\\\"description\\\":\\\"Ensures buttons have discernible text\\\",\\\"help\\\":\\\"Buttons must have discernible text\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/button-name?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"critical\\\",\\\"html\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#auth-button\\\"],\\\"failureSummary\\\":\\\"Fix all of the following:\\\\n  Element is in tab order and does not have accessible text\\\\n\\\\nFix any of the following:\\\\n  Element has a value attribute and the value attribute is empty\\\\n  Element has no value attribute or the value attribute is empty\\\\n  Element does not have inner text that is visible to screen readers\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Element's default semantics were not overridden with role=\\\\\\\"presentation\\\\\\\"\\\\n  Element's default semantics were not overridden with role=\\\\\\\"none\\\\\\\"\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,1,BUTTON\\\",\\\"snippet\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"button-name\\\",\\\"description\\\":\\\"Buttons do not have an accessible name\\\",\\\"helpText\\\":\\\"When a button doesn't have an accessible name, screen readers announce it as \\\\\\\"button\\\\\\\", making it unusable for users who rely on screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/button-name?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#auth-button\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,1,BUTTON\\\",\\\"snippet\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\"}]}},\\\"bypass\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"bypass\\\",\\\"description\\\":\\\"The page contains a heading, skip link, or landmark region\\\",\\\"helpText\\\":\\\"Adding ways to bypass repetitive content lets keyboard users navigate the page more efficiently. [Learn more](https://dequeuniversity.com/rules/axe/2.2/bypass?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"color-contrast\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"color-contrast\\\",\\\"impact\\\":\\\"serious\\\",\\\"tags\\\":[\\\"cat.color\\\",\\\"wcag2aa\\\",\\\"wcag143\\\"],\\\"description\\\":\\\"Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds\\\",\\\"help\\\":\\\"Elements must have sufficient color contrast\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/color-contrast?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">New</a>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#newest\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.12 (foreground color: #ffffff, background color: #2196f3, font size: 12.0pt, font weight: normal). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">\\\"},{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">SmartCash PWA</div>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#\\\\\\\\35 658560338853888 > div.pwa-name\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.53 (foreground color: #ffffff, background color: #268ce3, font size: 13.5pt, font weight: bold). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,24,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"},{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">APP Universidad de Colima</div>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#\\\\\\\\36 031687736623104 > div.pwa-name\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.4 (foreground color: #ffffff, background color: #669933, font size: 13.5pt, font weight: bold). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,29,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"color-contrast\\\",\\\"description\\\":\\\"Background and foreground colors do not have a sufficient contrast ratio.\\\",\\\"helpText\\\":\\\"Low-contrast text is difficult or impossible for many users to read. [Learn more](https://dequeuniversity.com/rules/axe/2.2/color-contrast?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#newest\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">\\\"},{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#\\\\\\\\35 658560338853888 > div.pwa-name\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,24,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"},{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#\\\\\\\\36 031687736623104 > div.pwa-name\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,29,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"}]}},\\\"definition-list\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"definition-list\\\",\\\"description\\\":\\\"`<dl>`'s do not contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` or `<template>` elements.\\\",\\\"helpText\\\":\\\"When definition lists are not properly marked up, screen readers may produce confusing or inaccurate output. [Learn more](https://dequeuniversity.com/rules/axe/2.2/definition-list?application=lighthouse).\\\"},\\\"dlitem\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"dlitem\\\",\\\"description\\\":\\\"Definition list items are not wrapped in `<dl>` elements\\\",\\\"helpText\\\":\\\"Definition list items (`<dt>` and `<dd>`) must be wrapped in a parent `<dl>` element to ensure that screen readers can properly announce them. [Learn more](https://dequeuniversity.com/rules/axe/2.2/dlitem?application=lighthouse).\\\"},\\\"document-title\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"document-title\\\",\\\"description\\\":\\\"Document has a `<title>` element\\\",\\\"helpText\\\":\\\"Screen reader users use page titles to get an overview of the contents of the page. [Learn more](https://dequeuniversity.com/rules/axe/2.2/document-title?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"duplicate-id\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"duplicate-id\\\",\\\"description\\\":\\\"`[id]` attributes on the page are unique\\\",\\\"helpText\\\":\\\"The value of an id attribute must be unique to prevent other instances from being overlooked by assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/duplicate-id?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"frame-title\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"frame-title\\\",\\\"description\\\":\\\"`<frame>` or `<iframe>` elements do not have a title\\\",\\\"helpText\\\":\\\"Screen reader users rely on frame titles to describe the contents of frames. [Learn more](https://dequeuniversity.com/rules/axe/2.2/frame-title?application=lighthouse).\\\"},\\\"html-has-lang\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"html-has-lang\\\",\\\"description\\\":\\\"`<html>` element has a `[lang]` attribute\\\",\\\"helpText\\\":\\\"If a page doesn't specify a lang attribute, a screen reader assumes that the page is in the default language that the user chose when setting up the screen reader. If the page isn't actually in the default language, then the screen reader might not announce the page's text correctly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/html-lang?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"html-lang-valid\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"html-lang-valid\\\",\\\"description\\\":\\\"`<html>` element has a valid value for its `[lang]` attribute\\\",\\\"helpText\\\":\\\"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) helps screen readers announce text properly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"image-alt\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"image-alt\\\",\\\"description\\\":\\\"Image elements have `[alt]` attributes\\\",\\\"helpText\\\":\\\"Informative elements should aim for short, descriptive alternate text. Decorative elements can be ignored with an empty alt attribute.[Learn more](https://dequeuniversity.com/rules/axe/2.2/image-alt?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"input-image-alt\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"input-image-alt\\\",\\\"description\\\":\\\"`<input type=\\\\\\\"image\\\\\\\">` elements do not have `[alt]` text\\\",\\\"helpText\\\":\\\"When an image is being used as an `<input>` button, providing alternative text can help screen reader users understand the purpose of the button. [Learn more](https://dequeuniversity.com/rules/axe/2.2/input-image-alt?application=lighthouse).\\\"},\\\"label\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"label\\\",\\\"impact\\\":\\\"critical\\\",\\\"tags\\\":[\\\"cat.forms\\\",\\\"wcag2a\\\",\\\"wcag332\\\",\\\"wcag131\\\",\\\"section508\\\",\\\"section508.22.n\\\"],\\\"description\\\":\\\"Ensures every form element has a label\\\",\\\"help\\\":\\\"Form elements must have labels\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/label?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"critical\\\",\\\"html\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#search-input\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Form element does not have an implicit (wrapped) <label>\\\\n  Form element does not have an explicit <label>\\\\n  Element has no title attribute or the title attribute is empty\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,0,FORM,0,DIV,0,INPUT\\\",\\\"snippet\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"label\\\",\\\"description\\\":\\\"Form elements do not have associated labels\\\",\\\"helpText\\\":\\\"Labels ensure that form controls are announced properly by assistive technologies, like screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/label?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#search-input\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,0,FORM,0,DIV,0,INPUT\\\",\\\"snippet\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\"}]}},\\\"layout-table\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"layout-table\\\",\\\"description\\\":\\\"Presentational `<table>` elements do not avoid using `<th>`, `<caption>` or the `[summary]` attribute.\\\",\\\"helpText\\\":\\\"A table being used for layout purposes should not include data elements, such as the th or caption elements or the summary attribute, because this can create a confusing experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/layout-table?application=lighthouse).\\\"},\\\"link-name\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"link-name\\\",\\\"impact\\\":\\\"serious\\\",\\\"tags\\\":[\\\"cat.name-role-value\\\",\\\"wcag2a\\\",\\\"wcag111\\\",\\\"wcag412\\\",\\\"wcag244\\\",\\\"section508\\\",\\\"section508.22.a\\\"],\\\"description\\\":\\\"Ensures links have discernible text\\\",\\\"help\\\":\\\"Links must have discernible text\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/link-name?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\"><svg fill=\\\\\\\"#1976D2\\\\\\\" height=\\\\\\\"48\\\\\\\" viewBox=\\\\\\\"0 0 24 24\\\\\\\" width=\\\\\\\"48\\\\\\\" xmlns=\\\\\\\"http://www.w3.org/2000/svg\\\\\\\"><path d=\\\\\\\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\\\\\\\"></path><path d=\\\\\\\"M0 0h24v24H0z\\\\\\\" fill=\\\\\\\"none\\\\\\\"></path></svg></a>\\\",\\\"element\\\":null,\\\"target\\\":[\\\".pager > a\\\"],\\\"failureSummary\\\":\\\"Fix all of the following:\\\\n  Element is in tab order and does not have accessible text\\\\n\\\\nFix any of the following:\\\\n  Element does not have text that is visible to screen readers\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Element's default semantics were not overridden with role=\\\\\\\"presentation\\\\\\\"\\\\n  Element's default semantics were not overridden with role=\\\\\\\"none\\\\\\\"\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"link-name\\\",\\\"description\\\":\\\"Links do not have a discernible name\\\",\\\"helpText\\\":\\\"Link text (and alternate text for images, when used as links) that is discernible, unique, and focusable improves the navigation experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/link-name?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\".pager > a\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\">\\\"}]}},\\\"list\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"list\\\",\\\"description\\\":\\\"Lists contain only `<li>` elements and script supporting elements (`<script>` and `<template>`).\\\",\\\"helpText\\\":\\\"Screen readers have a specific way of announcing lists. Ensuring proper list structure aids screen reader output. [Learn more](https://dequeuniversity.com/rules/axe/2.2/list?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"listitem\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"listitem\\\",\\\"description\\\":\\\"List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements\\\",\\\"helpText\\\":\\\"Screen readers require list items (`<li>`) to be contained within a parent `<ul>` or `<ol>` to be announced properly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/listitem?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"meta-refresh\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"meta-refresh\\\",\\\"description\\\":\\\"The document uses `<meta http-equiv=\\\\\\\"refresh\\\\\\\">`\\\",\\\"helpText\\\":\\\"Users do not expect a page to refresh automatically, and doing so will move focus back to the top of the page. This may create a frustrating or confusing experience. [Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-refresh?application=lighthouse).\\\"},\\\"meta-viewport\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"meta-viewport\\\",\\\"description\\\":\\\"`[user-scalable=\\\\\\\"no\\\\\\\"]` is not used in the `<meta name=\\\\\\\"viewport\\\\\\\">` element and the `[maximum-scale]` attribute is not less than 5.\\\",\\\"helpText\\\":\\\"Disabling zooming is problematic for users with low vision who rely on screen magnification to properly see the contents of a web page. [Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-viewport?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"object-alt\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"object-alt\\\",\\\"description\\\":\\\"`<object>` elements do not have `[alt]` text\\\",\\\"helpText\\\":\\\"Screen readers cannot translate non-text content. Adding alt text to `<object>` elements helps screen readers convey meaning to users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/object-alt?application=lighthouse).\\\"},\\\"tabindex\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"tabindex\\\",\\\"description\\\":\\\"Some elements have a `[tabindex]` value greater than 0\\\",\\\"helpText\\\":\\\"A value greater than 0 implies an explicit navigation ordering. Although technically valid, this often creates frustrating experiences for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/tabindex?application=lighthouse).\\\"},\\\"td-headers-attr\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"td-headers-attr\\\",\\\"description\\\":\\\"Cells in a `<table>` element that use the `[headers]` attribute refers to other cells of that same table.\\\",\\\"helpText\\\":\\\"Screen readers have features to make navigating tables easier. Ensuring `<td>` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/td-headers-attr?application=lighthouse).\\\"},\\\"th-has-data-cells\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"th-has-data-cells\\\",\\\"description\\\":\\\"`<th>` elements and elements with `[role=\\\\\\\"columnheader\\\\\\\"/\\\\\\\"rowheader\\\\\\\"]` do not have data cells they describe.\\\",\\\"helpText\\\":\\\"Screen readers have features to make navigating tables easier. Ensuring table headers always refer to some set of cells may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/th-has-data-cells?application=lighthouse).\\\"},\\\"valid-lang\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"valid-lang\\\",\\\"description\\\":\\\"`[lang]` attributes do not have a valid value\\\",\\\"helpText\\\":\\\"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) on elements helps ensure that text is pronounced correctly by a screen reader. [Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).\\\"},\\\"video-caption\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"video-caption\\\",\\\"description\\\":\\\"`<video>` elements do not contain a `<track>` element with `[kind=\\\\\\\"captions\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"When a video provides a caption it is easier for deaf and hearing impaired users to access its information. [Learn more](https://dequeuniversity.com/rules/axe/2.2/video-caption?application=lighthouse).\\\"},\\\"video-description\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"video-description\\\",\\\"description\\\":\\\"`<video>` elements do not contain a `<track>` element with `[kind=\\\\\\\"description\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"Audio descriptions provide relevant information for videos that dialogue cannot, such as facial expressions and scenes. [Learn more](https://dequeuniversity.com/rules/axe/2.2/video-description?application=lighthouse).\\\"},\\\"custom-controls-labels\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"custom-controls-labels\\\",\\\"description\\\":\\\"Custom controls have associated labels\\\",\\\"helpText\\\":\\\"Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"custom-controls-roles\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"custom-controls-roles\\\",\\\"description\\\":\\\"Custom controls have ARIA roles\\\",\\\"helpText\\\":\\\"Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"focus-traps\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"focus-traps\\\",\\\"description\\\":\\\"User focus is not accidentally trapped in a region\\\",\\\"helpText\\\":\\\"A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"focusable-controls\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"focusable-controls\\\",\\\"description\\\":\\\"Interactive controls are keyboard focusable\\\",\\\"helpText\\\":\\\"Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"heading-levels\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"heading-levels\\\",\\\"description\\\":\\\"Headings don't skip levels\\\",\\\"helpText\\\":\\\"Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).\\\"},\\\"logical-tab-order\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"logical-tab-order\\\",\\\"description\\\":\\\"The page has a logical tab order\\\",\\\"helpText\\\":\\\"Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"managed-focus\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"managed-focus\\\",\\\"description\\\":\\\"The user's focus is directed to new content added to the page\\\",\\\"helpText\\\":\\\"If new content, such as a dialog, is added to the page, the user's focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"offscreen-content-hidden\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"offscreen-content-hidden\\\",\\\"description\\\":\\\"Offscreen content is hidden from assistive technology\\\",\\\"helpText\\\":\\\"Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"use-landmarks\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"use-landmarks\\\",\\\"description\\\":\\\"HTML5 landmark elements are used to improve navigation\\\",\\\"helpText\\\":\\\"Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).\\\"},\\\"visual-order-follows-dom\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"visual-order-follows-dom\\\",\\\"description\\\":\\\"Visual order on the page follows DOM order\\\",\\\"helpText\\\":\\\"DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"uses-long-cache-ttl\\\":{\\\"score\\\":90,\\\"displayValue\\\":\\\"36 assets found\\\",\\\"rawValue\\\":115353.06250000003,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[{\\\"url\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\",\\\"cacheControl\\\":{\\\"private\\\":true,\\\"max-age\\\":1800,\\\"stale-while-revalidate\\\":\\\"1800\\\"},\\\"cacheLifetimeInSeconds\\\":1800,\\\"cacheLifetimeDisplay\\\":\\\"30 m\\\",\\\"cacheHitProbability\\\":0.1375,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5301,\\\"wastedBytes\\\":4572.1125},{\\\"url\\\":\\\"https://www.google-analytics.com/analytics.js\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":7200},\\\"cacheLifetimeInSeconds\\\":7200,\\\"cacheLifetimeDisplay\\\":\\\"2 h\\\",\\\"cacheHitProbability\\\":0.25,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalBytes\\\":14669,\\\"wastedBytes\\\":11001.75},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalBytes\\\":29577,\\\"wastedBytes\\\":11830.800000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"25 KB\\\",\\\"totalBytes\\\":25114,\\\"wastedBytes\\\":10045.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"17 KB\\\",\\\"totalBytes\\\":17090,\\\"wastedBytes\\\":6836},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalBytes\\\":15721,\\\"wastedBytes\\\":6288.400000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalBytes\\\":11708,\\\"wastedBytes\\\":4683.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalBytes\\\":11182,\\\"wastedBytes\\\":4472.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalBytes\\\":9504,\\\"wastedBytes\\\":3801.6000000000004},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5738966337716224.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalBytes\\\":8971,\\\"wastedBytes\\\":3588.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731517589356544.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":8171,\\\"wastedBytes\\\":3268.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5748197229068288.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7943,\\\"wastedBytes\\\":3177.2000000000003},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714605568425984.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7922,\\\"wastedBytes\\\":3168.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5764116273692672.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7918,\\\"wastedBytes\\\":3167.2000000000003},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5632451886972928.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"7 KB\\\",\\\"totalBytes\\\":6697,\\\"wastedBytes\\\":2678.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5730694817906688.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":6190,\\\"wastedBytes\\\":2476},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714576493510656.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":6187,\\\"wastedBytes\\\":2474.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5743887900475392.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":5981,\\\"wastedBytes\\\":2392.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5175833244205056.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5584,\\\"wastedBytes\\\":2233.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698283014979584.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5270,\\\"wastedBytes\\\":2108},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5662482835177472.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4414,\\\"wastedBytes\\\":1765.6000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5768852951531520.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4358,\\\"wastedBytes\\\":1743.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5751252863418368.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4144,\\\"wastedBytes\\\":1657.6000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5153237387706368.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4142,\\\"wastedBytes\\\":1656.8000000000002},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5135916992561152.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":3948,\\\"wastedBytes\\\":1579.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5115292022734848.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":3618,\\\"wastedBytes\\\":1447.2},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/img/lighthouse-36.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3393,\\\"wastedBytes\\\":1357.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5717926735773696.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3369,\\\"wastedBytes\\\":1347.6000000000001},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/img/GitHub-Mark-Light-48px.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3340,\\\"wastedBytes\\\":1336},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698555594407936.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3075,\\\"wastedBytes\\\":1230},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_4916230321340416.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":2995,\\\"wastedBytes\\\":1198},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658560338853888.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":2807,\\\"wastedBytes\\\":1122.8},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/favicons/android-chrome-72x72.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2539,\\\"wastedBytes\\\":1015.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5638230077603840.svg\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2489,\\\"wastedBytes\\\":995.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6310684802416640.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2361,\\\"wastedBytes\\\":944.4000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731555572973568.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":1726,\\\"wastedBytes\\\":690.4000000000001}],\\\"queryStringCount\\\":1}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"uses-long-cache-ttl\\\",\\\"description\\\":\\\"Uses inefficient cache policy on static assets\\\",\\\"helpText\\\":\\\"A long cache lifetime can speed up repeat visits to your page. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#cache-control).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Cache TTL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 m\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 h\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"17 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5738966337716224.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731517589356544.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5748197229068288.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714605568425984.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5764116273692672.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5632451886972928.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5730694817906688.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714576493510656.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5743887900475392.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5175833244205056.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698283014979584.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5662482835177472.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5768852951531520.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5751252863418368.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5153237387706368.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5135916992561152.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5115292022734848.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/img/lighthouse-36.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5717926735773696.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/img/GitHub-Mark-Light-48px.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698555594407936.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_4916230321340416.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658560338853888.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/favicons/android-chrome-72x72.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5638230077603840.svg\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6310684802416640.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731555572973568.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}]]}},\\\"total-byte-weight\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"Total size was 314 KB\\\",\\\"rawValue\\\":321912,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"totalBytes\\\":29577,\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalMs\\\":\\\"180 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"totalBytes\\\":25114,\\\"totalKb\\\":\\\"25 KB\\\",\\\"totalMs\\\":\\\"150 ms\\\"},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"totalBytes\\\":20538,\\\"totalKb\\\":\\\"20 KB\\\",\\\"totalMs\\\":\\\"120 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"totalBytes\\\":17090,\\\"totalKb\\\":\\\"17 KB\\\",\\\"totalMs\\\":\\\"100 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"totalBytes\\\":15721,\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\"},{\\\"url\\\":\\\"https://www.google-analytics.com/analytics.js\\\",\\\"totalBytes\\\":14669,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\"},{\\\"url\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\",\\\"totalBytes\\\":14190,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalMs\\\":\\\"80 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\",\\\"totalBytes\\\":11708,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalMs\\\":\\\"70 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\",\\\"totalBytes\\\":11182,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalMs\\\":\\\"70 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"totalBytes\\\":9504,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalMs\\\":\\\"60 ms\\\"}],\\\"totalCompletedRequests\\\":48}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"total-byte-weight\\\",\\\"description\\\":\\\"Avoids enormous network payloads\\\",\\\"helpText\\\":\\\"Large network payloads cost users real money and are highly correlated with long load times. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Total Size\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Transfer Time\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"180 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"150 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"120 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"17 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"100 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"90 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"90 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"80 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"70 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"70 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"60 ms\\\"}]]}},\\\"offscreen-images\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"offscreen-images\\\",\\\"description\\\":\\\"Offscreen images\\\",\\\"helpText\\\":\\\"Consider lazy-loading offscreen and hidden images to improve page load speed and time to interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).\\\"},\\\"unminified-css\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unminified-css\\\",\\\"description\\\":\\\"Minify CSS\\\",\\\"helpText\\\":\\\"Minifying CSS files can reduce network payload sizes.[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"unminified-javascript\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unminified-javascript\\\",\\\"description\\\":\\\"Minify JavaScript\\\",\\\"helpText\\\":\\\"Minifying JavaScript files can reduce payload sizes and script parse time. [Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"unused-css-rules\\\":{\\\"score\\\":90,\\\"displayValue\\\":\\\"Potential savings of 2 KB (~10 ms)\\\",\\\"rawValue\\\":10,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":10,\\\"wastedKb\\\":2,\\\"results\\\":[{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"wastedBytes\\\":2362,\\\"wastedPercent\\\":49.8300515705579,\\\"totalBytes\\\":4740,\\\"wastedKb\\\":\\\"2 KB\\\",\\\"wastedMs\\\":\\\"10 ms\\\",\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalMs\\\":\\\"30 ms\\\",\\\"potentialSavings\\\":\\\"2 KB (50%)\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unused-css-rules\\\",\\\"description\\\":\\\"Unused CSS rules\\\",\\\"helpText\\\":\\\"Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery)\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB (50%)\\\"}]]}},\\\"uses-webp-images\\\":{\\\"score\\\":65,\\\"displayValue\\\":\\\"Potential savings of 73 KB (~450 ms)\\\",\\\"rawValue\\\":450,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":450,\\\"wastedKb\\\":73,\\\"results\\\":[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":29329,\\\"wastedBytes\\\":22645,\\\"wastedKb\\\":\\\"22 KB\\\",\\\"wastedMs\\\":\\\"140 ms\\\",\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalMs\\\":\\\"180 ms\\\",\\\"potentialSavings\\\":\\\"22 KB (77%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":24862,\\\"wastedBytes\\\":20310,\\\"wastedKb\\\":\\\"20 KB\\\",\\\"wastedMs\\\":\\\"120 ms\\\",\\\"totalKb\\\":\\\"24 KB\\\",\\\"totalMs\\\":\\\"150 ms\\\",\\\"potentialSavings\\\":\\\"20 KB (82%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":15465,\\\"wastedBytes\\\":11923,\\\"wastedKb\\\":\\\"12 KB\\\",\\\"wastedMs\\\":\\\"70 ms\\\",\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\",\\\"potentialSavings\\\":\\\"12 KB (77%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":16834,\\\"wastedBytes\\\":11584,\\\"wastedKb\\\":\\\"11 KB\\\",\\\"wastedMs\\\":\\\"70 ms\\\",\\\"totalKb\\\":\\\"16 KB\\\",\\\"totalMs\\\":\\\"100 ms\\\",\\\"potentialSavings\\\":\\\"11 KB (69%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":9247,\\\"wastedBytes\\\":8201,\\\"wastedKb\\\":\\\"8 KB\\\",\\\"wastedMs\\\":\\\"50 ms\\\",\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalMs\\\":\\\"60 ms\\\",\\\"potentialSavings\\\":\\\"8 KB (89%)\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-webp-images\\\",\\\"description\\\":\\\"Serve images in next-gen formats\\\",\\\"helpText\\\":\\\"Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"22 KB (77%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"24 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 KB (82%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"12 KB (77%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"16 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB (69%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB (89%)\\\"}]]}},\\\"uses-optimized-images\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-optimized-images\\\",\\\"description\\\":\\\"Optimize images\\\",\\\"helpText\\\":\\\"Optimized images load faster and consume less cellular data. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"uses-request-compression\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-request-compression\\\",\\\"description\\\":\\\"Enable text compression\\\",\\\"helpText\\\":\\\"Text-based responses should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Uncompressed resource URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"GZIP Savings\\\"}],\\\"items\\\":[]}},\\\"uses-responsive-images\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-responsive-images\\\",\\\"description\\\":\\\"Properly size images\\\",\\\"helpText\\\":\\\"Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"appcache-manifest\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"appcache-manifest\\\",\\\"description\\\":\\\"Avoids Application Cache\\\",\\\"helpText\\\":\\\"Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).\\\"},\\\"dom-size\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"244 nodes\\\",\\\"rawValue\\\":244,\\\"extendedInfo\\\":{\\\"value\\\":[{\\\"title\\\":\\\"Total DOM Nodes\\\",\\\"value\\\":\\\"244\\\",\\\"target\\\":\\\"< 1,500 nodes\\\"},{\\\"title\\\":\\\"DOM Depth\\\",\\\"value\\\":\\\"9\\\",\\\"snippet\\\":\\\"html >\\\\n  body >\\\\n    div.section.primary-background.box-shadow >\\\\n      div.section-top >\\\\n        form#search.search-form >\\\\n          div.search-group >\\\\n            a#search-icon >\\\\n              svg >\\\\n                path\\\",\\\"target\\\":\\\"< 32\\\"},{\\\"title\\\":\\\"Maximum Children\\\",\\\"value\\\":\\\"36\\\",\\\"snippet\\\":\\\"Element with most children:\\\\nhead\\\",\\\"target\\\":\\\"< 60 nodes\\\"}]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"dom-size\\\",\\\"description\\\":\\\"Avoids an excessive DOM size\\\",\\\"helpText\\\":\\\"Browser engineers recommend pages contain fewer than ~1,500 DOM nodes. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/fundamentals/performance/rendering/).\\\",\\\"details\\\":{\\\"type\\\":\\\"cards\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View details\\\"},\\\"items\\\":[{\\\"title\\\":\\\"Total DOM Nodes\\\",\\\"value\\\":\\\"244\\\",\\\"target\\\":\\\"< 1,500 nodes\\\"},{\\\"title\\\":\\\"DOM Depth\\\",\\\"value\\\":\\\"9\\\",\\\"snippet\\\":\\\"html >\\\\n  body >\\\\n    div.section.primary-background.box-shadow >\\\\n      div.section-top >\\\\n        form#search.search-form >\\\\n          div.search-group >\\\\n            a#search-icon >\\\\n              svg >\\\\n                path\\\",\\\"target\\\":\\\"< 32\\\"},{\\\"title\\\":\\\"Maximum Children\\\",\\\"value\\\":\\\"36\\\",\\\"snippet\\\":\\\"Element with most children:\\\\nhead\\\",\\\"target\\\":\\\"< 60 nodes\\\"}]}},\\\"external-anchors-use-rel-noopener\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"external-anchors-use-rel-noopener\\\",\\\"description\\\":\\\"Opens external anchors using `rel=\\\\\\\"noopener\\\\\\\"`\\\",\\\"helpText\\\":\\\"Open new tabs using `rel=\\\\\\\"noopener\\\\\\\"` to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Target\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Rel\\\"}],\\\"items\\\":[]}},\\\"geolocation-on-start\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"geolocation-on-start\\\",\\\"description\\\":\\\"Avoids requesting the geolocation permission on page load\\\",\\\"helpText\\\":\\\"Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"link-blocking-first-paint\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":\\\"0 ms\\\",\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"link-blocking-first-paint\\\",\\\"description\\\":\\\"Reduce render-blocking stylesheets\\\",\\\"helpText\\\":\\\"External stylesheets are blocking the first paint of your page. Consider delivering critical CSS via `<style>` tags and deferring non-critical styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Delayed Paint By (ms)\\\"}],\\\"items\\\":[]}},\\\"no-document-write\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-document-write\\\",\\\"description\\\":\\\"Avoids `document.write()`\\\",\\\"helpText\\\":\\\"For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"no-mutation-events\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-mutation-events\\\",\\\"description\\\":\\\"Avoids Mutation Events in its own scripts\\\",\\\"helpText\\\":\\\"Mutation Events are deprecated and harm performance. Consider using Mutation Observers instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/mutation-events).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Event\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Line\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Col\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Snippet\\\"}],\\\"items\\\":[]}},\\\"no-vulnerable-libraries\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-vulnerable-libraries\\\",\\\"description\\\":\\\"Avoids front-end JavaScript libraries with known security vulnerabilities\\\",\\\"helpText\\\":\\\"Some third-party scripts may contain known security vulnerabilities  that are easily identified and exploited by attackers.\\\"},\\\"no-websql\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-websql\\\",\\\"description\\\":\\\"Avoids WebSQL DB\\\",\\\"helpText\\\":\\\"Web SQL is deprecated. Consider using IndexedDB instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/web-sql).\\\"},\\\"notification-on-start\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"notification-on-start\\\",\\\"description\\\":\\\"Avoids requesting the notification permission on page load\\\",\\\"helpText\\\":\\\"Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"password-inputs-can-be-pasted-into\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"password-inputs-can-be-pasted-into\\\",\\\"description\\\":\\\"Allows users to paste into password fields\\\",\\\"helpText\\\":\\\"Preventing password pasting undermines good security policy. [Learn more](https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords)\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Password inputs that prevent pasting into\\\"},\\\"items\\\":[]}},\\\"script-blocking-first-paint\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":\\\"0 ms\\\",\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"script-blocking-first-paint\\\",\\\"description\\\":\\\"Reduce render-blocking scripts\\\",\\\"helpText\\\":\\\"Script elements are blocking the first paint of your page. Consider inlining critical scripts and deferring non-critical ones. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Delayed Paint By (ms)\\\"}],\\\"items\\\":[]}},\\\"uses-http2\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"uses-http2\\\",\\\"description\\\":\\\"Uses HTTP/2 for its own resources\\\",\\\"helpText\\\":\\\"HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Protocol\\\"}],\\\"items\\\":[]}},\\\"uses-passive-event-listeners\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"uses-passive-event-listeners\\\",\\\"description\\\":\\\"Uses passive listeners to improve scrolling performance\\\",\\\"helpText\\\":\\\"Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"meta-description\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"meta-description\\\",\\\"description\\\":\\\"Document has a meta description\\\",\\\"helpText\\\":\\\"Meta descriptions may be included in search results to concisely summarize page content. [Learn more](https://support.google.com/webmasters/answer/35624?hl=en#1).\\\"},\\\"http-status-code\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"http-status-code\\\",\\\"description\\\":\\\"Page has successful HTTP status code\\\",\\\"helpText\\\":\\\"Pages with unsuccessful HTTP status codes may not be indexed properly. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).\\\"},\\\"font-size\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":null,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"font-size\\\",\\\"description\\\":\\\"Document uses legible font sizes\\\",\\\"helpText\\\":\\\"Font sizes less than 16px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >75% of page text ≥16px. [Learn more](https://developers.google.com/speed/docs/insights/UseLegibleFontSizes).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Source\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Selector\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"% of Page Text\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Font Size\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"Legible text\\\"},{\\\"type\\\":\\\"code\\\",\\\"text\\\":null},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"100.00%\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"≥ 16px\\\"}]]}},\\\"link-text\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"link-text\\\",\\\"description\\\":\\\"Links have descriptive text\\\",\\\"helpText\\\":\\\"Descriptive link text helps search engines understand your content. [Learn more](https://webmasters.googleblog.com/2008/10/importance-of-link-architecture.html).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Link destination\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Link Text\\\"}],\\\"items\\\":[]}},\\\"is-crawlable\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"is-crawlable\\\",\\\"description\\\":\\\"Page isn’t blocked from indexing\\\",\\\"helpText\\\":\\\"The \\\\\\\"Robots\\\\\\\" directives tell crawlers how your content should be indexed. [Learn more](https://developers.google.com/search/reference/robots_meta_tag).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Source\\\"}],\\\"items\\\":[]}},\\\"hreflang\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"hreflang\\\",\\\"description\\\":\\\"Document has a valid `hreflang`\\\",\\\"helpText\\\":\\\"hreflang allows crawlers to discover alternate translations of the page content. [Learn more](https://support.google.com/webmasters/answer/189077).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Source\\\"}],\\\"items\\\":[]}},\\\"plugins\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"plugins\\\",\\\"description\\\":\\\"Document avoids plugins\\\",\\\"helpText\\\":\\\"Most mobile devices do not support plugins, and many desktop browsers restrict them.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Element source\\\"}],\\\"items\\\":[]}},\\\"canonical\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"canonical\\\",\\\"description\\\":\\\"Document has a valid `rel=canonical`\\\",\\\"helpText\\\":\\\"Canonical links suggest which URL to show in search results. Read more in [Use canonical URLs](https://support.google.com/webmasters/answer/139066).\\\"},\\\"mobile-friendly\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"mobile-friendly\\\",\\\"description\\\":\\\"Page is mobile friendly\\\",\\\"helpText\\\":\\\"Take the [Mobile-Friendly Test](https://search.google.com/test/mobile-friendly) to check for audits not covered by Lighthouse, like sizing tap targets appropriately. [Learn more](https://developers.google.com/search/mobile-sites/).\\\"},\\\"structured-data\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"structured-data\\\",\\\"description\\\":\\\"Structured data is valid\\\",\\\"helpText\\\":\\\"Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).\\\"}},\\\"runtimeConfig\\\":{\\\"environment\\\":[{\\\"name\\\":\\\"Device Emulation\\\",\\\"enabled\\\":true,\\\"description\\\":\\\"Nexus 5X\\\"},{\\\"name\\\":\\\"Network Throttling\\\",\\\"enabled\\\":true,\\\"description\\\":\\\"562.5ms RTT, 1.4Mbps down, 0.7Mbps up\\\"},{\\\"name\\\":\\\"CPU Throttling\\\",\\\"enabled\\\":true,\\\"description\\\":\\\"4x slowdown\\\"}],\\\"blockedUrlPatterns\\\":[],\\\"extraHeaders\\\":{}},\\\"score\\\":90.9090909090909,\\\"reportCategories\\\":[{\\\"name\\\":\\\"Performance\\\",\\\"description\\\":\\\"These encapsulate your web app's current performance and opportunities to improve it.\\\",\\\"audits\\\":[{\\\"id\\\":\\\"first-meaningful-paint\\\",\\\"weight\\\":5,\\\"group\\\":\\\"perf-metric\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"1,040 ms\\\",\\\"rawValue\\\":1037.2,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"timestamps\\\":{\\\"navStart\\\":2152463602,\\\"fCP\\\":2153500775,\\\"fMP\\\":2153500778,\\\"onLoad\\\":2156528062,\\\"endOfTrace\\\":2161852046},\\\"timings\\\":{\\\"navStart\\\":0,\\\"fCP\\\":1037.173,\\\"fMP\\\":1037.176,\\\"onLoad\\\":4064.46,\\\"endOfTrace\\\":9388.444},\\\"fmpFellBack\\\":false}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"first-meaningful-paint\\\",\\\"description\\\":\\\"First meaningful paint\\\",\\\"helpText\\\":\\\"First meaningful paint measures when the primary content of a page is visible. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-meaningful-paint).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"first-interactive\\\",\\\"weight\\\":5,\\\"group\\\":\\\"perf-metric\\\",\\\"result\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"first-interactive\\\",\\\"description\\\":\\\"First Interactive (beta)\\\",\\\"helpText\\\":\\\"First Interactive marks the time at which the page is minimally interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/first-interactive).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"consistently-interactive\\\",\\\"weight\\\":5,\\\"group\\\":\\\"perf-metric\\\",\\\"result\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_TTI_CPU_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"consistently-interactive\\\",\\\"description\\\":\\\"Consistently Interactive (beta)\\\",\\\"helpText\\\":\\\"Consistently Interactive marks the time at which the page is fully interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/consistently-interactive).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"speed-index-metric\\\",\\\"weight\\\":1,\\\"group\\\":\\\"perf-metric\\\",\\\"result\\\":{\\\"score\\\":99,\\\"displayValue\\\":\\\"1,142\\\",\\\"rawValue\\\":1142,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"timings\\\":{\\\"firstVisualChange\\\":1037,\\\"visuallyReady\\\":1037.3799999998882,\\\"visuallyComplete\\\":2898,\\\"perceptualSpeedIndex\\\":1141.835581201553},\\\"timestamps\\\":{\\\"firstVisualChange\\\":2153500602,\\\"visuallyReady\\\":2153500982,\\\"visuallyComplete\\\":2155361602,\\\"perceptualSpeedIndex\\\":2153605437.5812016},\\\"frames\\\":[{\\\"timestamp\\\":2152463.602,\\\"progress\\\":0},{\\\"timestamp\\\":2153500.982,\\\"progress\\\":90.31905946956357},{\\\"timestamp\\\":2153832.606,\\\"progress\\\":90.31905946956357},{\\\"timestamp\\\":2153849.478,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153870.388,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153883.379,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153921.808,\\\"progress\\\":90.33605036713934},{\\\"timestamp\\\":2153932.306,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2153983.395,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2153999.242,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2154046.561,\\\"progress\\\":92.35056734420138},{\\\"timestamp\\\":2154066.125,\\\"progress\\\":95.22683766928108},{\\\"timestamp\\\":2154082.415,\\\"progress\\\":95.22683766928108},{\\\"timestamp\\\":2154118.314,\\\"progress\\\":95.23115222717236},{\\\"timestamp\\\":2154962.536,\\\"progress\\\":97.56635937215954},{\\\"timestamp\\\":2155362.197,\\\"progress\\\":100}]}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"speed-index-metric\\\",\\\"description\\\":\\\"Perceptual Speed Index\\\",\\\"helpText\\\":\\\"Speed Index shows how quickly the contents of a page are visibly populated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/speed-index).\\\"},\\\"score\\\":99},{\\\"id\\\":\\\"estimated-input-latency\\\",\\\"weight\\\":1,\\\"group\\\":\\\"perf-metric\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"21 ms\\\",\\\"rawValue\\\":20.8,\\\"extendedInfo\\\":{\\\"value\\\":[{\\\"percentile\\\":0.5,\\\"time\\\":16},{\\\"percentile\\\":0.75,\\\"time\\\":16},{\\\"percentile\\\":0.9,\\\"time\\\":20.799407692307533},{\\\"percentile\\\":0.99,\\\"time\\\":90.00607999999784},{\\\"percentile\\\":1,\\\"time\\\":128.2549999999901}]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"estimated-input-latency\\\",\\\"description\\\":\\\"Estimated Input Latency\\\",\\\"helpText\\\":\\\"The score above is an estimate of how long your app takes to respond to user input, in milliseconds. There is a 90% probability that a user encounters this amount of latency, or less. 10% of the time a user can expect additional latency. If your latency is higher than 50 ms, users may perceive your app as laggy. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/estimated-input-latency).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"link-blocking-first-paint\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":\\\"0 ms\\\",\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"link-blocking-first-paint\\\",\\\"description\\\":\\\"Reduce render-blocking stylesheets\\\",\\\"helpText\\\":\\\"External stylesheets are blocking the first paint of your page. Consider delivering critical CSS via `<style>` tags and deferring non-critical styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Delayed Paint By (ms)\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"script-blocking-first-paint\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":\\\"0 ms\\\",\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"script-blocking-first-paint\\\",\\\"description\\\":\\\"Reduce render-blocking scripts\\\",\\\"helpText\\\":\\\"Script elements are blocking the first paint of your page. Consider inlining critical scripts and deferring non-critical ones. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Delayed Paint By (ms)\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-responsive-images\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-responsive-images\\\",\\\"description\\\":\\\"Properly size images\\\",\\\"helpText\\\":\\\"Serve images that are appropriately-sized to save cellular data and improve load time. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/oversized-images).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"offscreen-images\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"offscreen-images\\\",\\\"description\\\":\\\"Offscreen images\\\",\\\"helpText\\\":\\\"Consider lazy-loading offscreen and hidden images to improve page load speed and time to interactive. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"unminified-css\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unminified-css\\\",\\\"description\\\":\\\"Minify CSS\\\",\\\"helpText\\\":\\\"Minifying CSS files can reduce network payload sizes.[Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"unminified-javascript\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unminified-javascript\\\",\\\"description\\\":\\\"Minify JavaScript\\\",\\\"helpText\\\":\\\"Minifying JavaScript files can reduce payload sizes and script parse time. [Learn more](https://developers.google.com/speed/docs/insights/MinifyResources).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"unused-css-rules\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":90,\\\"displayValue\\\":\\\"Potential savings of 2 KB (~10 ms)\\\",\\\"rawValue\\\":10,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":10,\\\"wastedKb\\\":2,\\\"results\\\":[{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"wastedBytes\\\":2362,\\\"wastedPercent\\\":49.8300515705579,\\\"totalBytes\\\":4740,\\\"wastedKb\\\":\\\"2 KB\\\",\\\"wastedMs\\\":\\\"10 ms\\\",\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalMs\\\":\\\"30 ms\\\",\\\"potentialSavings\\\":\\\"2 KB (50%)\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"unused-css-rules\\\",\\\"description\\\":\\\"Unused CSS rules\\\",\\\"helpText\\\":\\\"Remove unused rules from stylesheets to reduce unnecessary bytes consumed by network activity. [Learn more](https://developers.google.com/speed/docs/insights/OptimizeCSSDelivery)\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB (50%)\\\"}]]}},\\\"score\\\":90},{\\\"id\\\":\\\"uses-optimized-images\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-optimized-images\\\",\\\"description\\\":\\\"Optimize images\\\",\\\"helpText\\\":\\\"Optimized images load faster and consume less cellular data. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/optimize-images).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-webp-images\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":65,\\\"displayValue\\\":\\\"Potential savings of 73 KB (~450 ms)\\\",\\\"rawValue\\\":450,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":450,\\\"wastedKb\\\":73,\\\"results\\\":[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":29329,\\\"wastedBytes\\\":22645,\\\"wastedKb\\\":\\\"22 KB\\\",\\\"wastedMs\\\":\\\"140 ms\\\",\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalMs\\\":\\\"180 ms\\\",\\\"potentialSavings\\\":\\\"22 KB (77%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":24862,\\\"wastedBytes\\\":20310,\\\"wastedKb\\\":\\\"20 KB\\\",\\\"wastedMs\\\":\\\"120 ms\\\",\\\"totalKb\\\":\\\"24 KB\\\",\\\"totalMs\\\":\\\"150 ms\\\",\\\"potentialSavings\\\":\\\"20 KB (82%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":15465,\\\"wastedBytes\\\":11923,\\\"wastedKb\\\":\\\"12 KB\\\",\\\"wastedMs\\\":\\\"70 ms\\\",\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\",\\\"potentialSavings\\\":\\\"12 KB (77%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":16834,\\\"wastedBytes\\\":11584,\\\"wastedKb\\\":\\\"11 KB\\\",\\\"wastedMs\\\":\\\"70 ms\\\",\\\"totalKb\\\":\\\"16 KB\\\",\\\"totalMs\\\":\\\"100 ms\\\",\\\"potentialSavings\\\":\\\"11 KB (69%)\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"fromProtocol\\\":true,\\\"isCrossOrigin\\\":true,\\\"preview\\\":{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},\\\"totalBytes\\\":9247,\\\"wastedBytes\\\":8201,\\\"wastedKb\\\":\\\"8 KB\\\",\\\"wastedMs\\\":\\\"50 ms\\\",\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalMs\\\":\\\"60 ms\\\",\\\"potentialSavings\\\":\\\"8 KB (89%)\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-webp-images\\\",\\\"description\\\":\\\"Serve images in next-gen formats\\\",\\\"helpText\\\":\\\"Image formats like JPEG 2000, JPEG XR, and WebP often provide better compression than PNG or JPEG, which means faster downloads and less data consumption. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/webp).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"22 KB (77%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"24 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 KB (82%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"12 KB (77%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"16 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB (69%)\\\"}],[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"mimeType\\\":\\\"image/png\\\",\\\"type\\\":\\\"thumbnail\\\"},{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB (89%)\\\"}]]}},\\\"score\\\":65},{\\\"id\\\":\\\"uses-request-compression\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0,\\\"wastedKb\\\":0,\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-request-compression\\\",\\\"description\\\":\\\"Enable text compression\\\",\\\"helpText\\\":\\\"Text-based responses should be served with compression (gzip, deflate or brotli) to minimize total network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/optimize-encoding-and-transfer).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Uncompressed resource URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Original\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"GZIP Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"time-to-first-byte\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"560 ms\\\",\\\"rawValue\\\":564.1660000001137,\\\"debugString\\\":\\\"\\\",\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":-35.83399999988626}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"time-to-first-byte\\\",\\\"description\\\":\\\"Keep server response times low (TTFB)\\\",\\\"helpText\\\":\\\"Time To First Byte identifies the time at which your server sends a response. [Learn more](https://developers.google.com/web/tools/chrome-devtools/network-performance/issues).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"redirects\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"0 ms\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"wastedMs\\\":0}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"redirects\\\",\\\"description\\\":\\\"Avoids page redirects\\\",\\\"helpText\\\":\\\"Redirects introduce additional delays before the page can be loaded. [Learn more](https://developers.google.com/speed/docs/insights/AvoidRedirects).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Redirected URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time for Redirect\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-rel-preload\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-hint\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"0 ms\\\",\\\"rawValue\\\":0,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"informative\\\":true,\\\"name\\\":\\\"uses-rel-preload\\\",\\\"description\\\":\\\"Preload key requests\\\",\\\"helpText\\\":\\\"Consider using <link rel=preload> to prioritize fetching late-discovered resources sooner [Learn more](https://developers.google.com/web/updates/2016/03/link-rel-preload).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Potential Savings\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"total-byte-weight\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"Total size was 314 KB\\\",\\\"rawValue\\\":321912,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"totalBytes\\\":29577,\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalMs\\\":\\\"180 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"totalBytes\\\":25114,\\\"totalKb\\\":\\\"25 KB\\\",\\\"totalMs\\\":\\\"150 ms\\\"},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"totalBytes\\\":20538,\\\"totalKb\\\":\\\"20 KB\\\",\\\"totalMs\\\":\\\"120 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"totalBytes\\\":17090,\\\"totalKb\\\":\\\"17 KB\\\",\\\"totalMs\\\":\\\"100 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"totalBytes\\\":15721,\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\"},{\\\"url\\\":\\\"https://www.google-analytics.com/analytics.js\\\",\\\"totalBytes\\\":14669,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalMs\\\":\\\"90 ms\\\"},{\\\"url\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\",\\\"totalBytes\\\":14190,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalMs\\\":\\\"80 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\",\\\"totalBytes\\\":11708,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalMs\\\":\\\"70 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\",\\\"totalBytes\\\":11182,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalMs\\\":\\\"70 ms\\\"},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"totalBytes\\\":9504,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalMs\\\":\\\"60 ms\\\"}],\\\"totalCompletedRequests\\\":48}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"total-byte-weight\\\",\\\"description\\\":\\\"Avoids enormous network payloads\\\",\\\"helpText\\\":\\\"Large network payloads cost users real money and are highly correlated with long load times. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Total Size\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Transfer Time\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"180 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"150 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"120 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"17 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"100 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"90 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"90 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"80 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"70 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"70 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"60 ms\\\"}]]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-long-cache-ttl\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":90,\\\"displayValue\\\":\\\"36 assets found\\\",\\\"rawValue\\\":115353.06250000003,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[{\\\"url\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\",\\\"cacheControl\\\":{\\\"private\\\":true,\\\"max-age\\\":1800,\\\"stale-while-revalidate\\\":\\\"1800\\\"},\\\"cacheLifetimeInSeconds\\\":1800,\\\"cacheLifetimeDisplay\\\":\\\"30 m\\\",\\\"cacheHitProbability\\\":0.1375,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5301,\\\"wastedBytes\\\":4572.1125},{\\\"url\\\":\\\"https://www.google-analytics.com/analytics.js\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":7200},\\\"cacheLifetimeInSeconds\\\":7200,\\\"cacheLifetimeDisplay\\\":\\\"2 h\\\",\\\"cacheHitProbability\\\":0.25,\\\"totalKb\\\":\\\"14 KB\\\",\\\"totalBytes\\\":14669,\\\"wastedBytes\\\":11001.75},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"29 KB\\\",\\\"totalBytes\\\":29577,\\\"wastedBytes\\\":11830.800000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"25 KB\\\",\\\"totalBytes\\\":25114,\\\"wastedBytes\\\":10045.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"17 KB\\\",\\\"totalBytes\\\":17090,\\\"wastedBytes\\\":6836},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"15 KB\\\",\\\"totalBytes\\\":15721,\\\"wastedBytes\\\":6288.400000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalBytes\\\":11708,\\\"wastedBytes\\\":4683.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"11 KB\\\",\\\"totalBytes\\\":11182,\\\"wastedBytes\\\":4472.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalBytes\\\":9504,\\\"wastedBytes\\\":3801.6000000000004},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5738966337716224.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"9 KB\\\",\\\"totalBytes\\\":8971,\\\"wastedBytes\\\":3588.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731517589356544.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":8171,\\\"wastedBytes\\\":3268.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5748197229068288.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7943,\\\"wastedBytes\\\":3177.2000000000003},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714605568425984.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7922,\\\"wastedBytes\\\":3168.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5764116273692672.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"8 KB\\\",\\\"totalBytes\\\":7918,\\\"wastedBytes\\\":3167.2000000000003},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5632451886972928.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"7 KB\\\",\\\"totalBytes\\\":6697,\\\"wastedBytes\\\":2678.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5730694817906688.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":6190,\\\"wastedBytes\\\":2476},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714576493510656.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":6187,\\\"wastedBytes\\\":2474.8},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5743887900475392.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"6 KB\\\",\\\"totalBytes\\\":5981,\\\"wastedBytes\\\":2392.4},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5175833244205056.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5584,\\\"wastedBytes\\\":2233.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698283014979584.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"5 KB\\\",\\\"totalBytes\\\":5270,\\\"wastedBytes\\\":2108},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5662482835177472.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4414,\\\"wastedBytes\\\":1765.6000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5768852951531520.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4358,\\\"wastedBytes\\\":1743.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5751252863418368.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4144,\\\"wastedBytes\\\":1657.6000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5153237387706368.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":4142,\\\"wastedBytes\\\":1656.8000000000002},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5135916992561152.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":3948,\\\"wastedBytes\\\":1579.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5115292022734848.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"4 KB\\\",\\\"totalBytes\\\":3618,\\\"wastedBytes\\\":1447.2},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/img/lighthouse-36.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3393,\\\"wastedBytes\\\":1357.2},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5717926735773696.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3369,\\\"wastedBytes\\\":1347.6000000000001},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/img/GitHub-Mark-Light-48px.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3340,\\\"wastedBytes\\\":1336},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698555594407936.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":3075,\\\"wastedBytes\\\":1230},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_4916230321340416.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":2995,\\\"wastedBytes\\\":1198},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658560338853888.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"3 KB\\\",\\\"totalBytes\\\":2807,\\\"wastedBytes\\\":1122.8},{\\\"url\\\":\\\"https://pwa-directory.appspot.com/favicons/android-chrome-72x72.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2539,\\\"wastedBytes\\\":1015.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5638230077603840.svg\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2489,\\\"wastedBytes\\\":995.6},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6310684802416640.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":2361,\\\"wastedBytes\\\":944.4000000000001},{\\\"url\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731555572973568.png\\\",\\\"cacheControl\\\":{\\\"public\\\":true,\\\"max-age\\\":86400},\\\"cacheLifetimeInSeconds\\\":86400,\\\"cacheLifetimeDisplay\\\":\\\"1 d\\\",\\\"cacheHitProbability\\\":0.6,\\\"totalKb\\\":\\\"2 KB\\\",\\\"totalBytes\\\":1726,\\\"wastedBytes\\\":690.4000000000001}],\\\"queryStringCount\\\":1}},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"uses-long-cache-ttl\\\",\\\"description\\\":\\\"Uses inefficient cache policy on static assets\\\",\\\"helpText\\\":\\\"A long cache lifetime can speed up repeat visits to your page. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching#cache-control).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Cache TTL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Size (KB)\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 m\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 h\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"14 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5716632776212480.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658238098866176.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6031687736623104.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"17 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6295542224125952.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"15 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5753679217950720.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6300111297576960.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5707558047186944.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5738966337716224.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"9 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731517589356544.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5748197229068288.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714605568425984.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5764116273692672.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"8 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5632451886972928.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5730694817906688.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5714576493510656.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5743887900475392.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5175833244205056.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698283014979584.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5662482835177472.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5768852951531520.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5751252863418368.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5153237387706368.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5135916992561152.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5115292022734848.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/img/lighthouse-36.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5717926735773696.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/img/GitHub-Mark-Light-48px.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5698555594407936.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_4916230321340416.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5658560338853888.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"3 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/favicons/android-chrome-72x72.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5638230077603840.svg\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_6310684802416640.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://storage.googleapis.com/pwa-directory.appspot.com/128_5731555572973568.png\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"1 d\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"2 KB\\\"}]]}},\\\"score\\\":90},{\\\"id\\\":\\\"dom-size\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"244 nodes\\\",\\\"rawValue\\\":244,\\\"extendedInfo\\\":{\\\"value\\\":[{\\\"title\\\":\\\"Total DOM Nodes\\\",\\\"value\\\":\\\"244\\\",\\\"target\\\":\\\"< 1,500 nodes\\\"},{\\\"title\\\":\\\"DOM Depth\\\",\\\"value\\\":\\\"9\\\",\\\"snippet\\\":\\\"html >\\\\n  body >\\\\n    div.section.primary-background.box-shadow >\\\\n      div.section-top >\\\\n        form#search.search-form >\\\\n          div.search-group >\\\\n            a#search-icon >\\\\n              svg >\\\\n                path\\\",\\\"target\\\":\\\"< 32\\\"},{\\\"title\\\":\\\"Maximum Children\\\",\\\"value\\\":\\\"36\\\",\\\"snippet\\\":\\\"Element with most children:\\\\nhead\\\",\\\"target\\\":\\\"< 60 nodes\\\"}]},\\\"scoringMode\\\":\\\"numeric\\\",\\\"name\\\":\\\"dom-size\\\",\\\"description\\\":\\\"Avoids an excessive DOM size\\\",\\\"helpText\\\":\\\"Browser engineers recommend pages contain fewer than ~1,500 DOM nodes. The sweet spot is a tree depth < 32 elements and fewer than 60 children/parent element. A large DOM can increase memory usage, cause longer [style calculations](https://developers.google.com/web/fundamentals/performance/rendering/reduce-the-scope-and-complexity-of-style-calculations), and produce costly [layout reflows](https://developers.google.com/speed/articles/reflow). [Learn more](https://developers.google.com/web/fundamentals/performance/rendering/).\\\",\\\"details\\\":{\\\"type\\\":\\\"cards\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View details\\\"},\\\"items\\\":[{\\\"title\\\":\\\"Total DOM Nodes\\\",\\\"value\\\":\\\"244\\\",\\\"target\\\":\\\"< 1,500 nodes\\\"},{\\\"title\\\":\\\"DOM Depth\\\",\\\"value\\\":\\\"9\\\",\\\"snippet\\\":\\\"html >\\\\n  body >\\\\n    div.section.primary-background.box-shadow >\\\\n      div.section-top >\\\\n        form#search.search-form >\\\\n          div.search-group >\\\\n            a#search-icon >\\\\n              svg >\\\\n                path\\\",\\\"target\\\":\\\"< 32\\\"},{\\\"title\\\":\\\"Maximum Children\\\",\\\"value\\\":\\\"36\\\",\\\"snippet\\\":\\\"Element with most children:\\\\nhead\\\",\\\"target\\\":\\\"< 60 nodes\\\"}]}},\\\"score\\\":100},{\\\"id\\\":\\\"critical-request-chains\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"0\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"chains\\\":{\\\"2047.1\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"startTime\\\":2152.467431,\\\"endTime\\\":2153.09461,\\\"responseReceivedTime\\\":2153.061376,\\\"transferSize\\\":6367},\\\"children\\\":{}},\\\"2047.2\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"startTime\\\":2153.127787,\\\"endTime\\\":2153.236907,\\\"responseReceivedTime\\\":2153.188976,\\\"transferSize\\\":4740},\\\"children\\\":{}},\\\"2047.3\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"startTime\\\":2153.128558,\\\"endTime\\\":2153.473274,\\\"responseReceivedTime\\\":2153.292108,\\\"transferSize\\\":20538},\\\"children\\\":{}}},\\\"longestChain\\\":{\\\"duration\\\":1005.8429999999134,\\\"length\\\":1,\\\"transferSize\\\":20538}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"critical-request-chains\\\",\\\"description\\\":\\\"Critical Request Chains\\\",\\\"helpText\\\":\\\"The Critical Request Chains below show you what resources are issued with a high priority. Consider reducing the length of chains, reducing the download size of resources, or deferring the download of unnecessary resources to improve page load. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/critical-request-chains).\\\",\\\"details\\\":{\\\"type\\\":\\\"criticalrequestchain\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View critical network waterfall:\\\"},\\\"chains\\\":{\\\"2047.1\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/\\\",\\\"startTime\\\":2152.467431,\\\"endTime\\\":2153.09461,\\\"responseReceivedTime\\\":2153.061376,\\\"transferSize\\\":6367},\\\"children\\\":{}},\\\"2047.2\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/css/style.73cd99ab03.css\\\",\\\"startTime\\\":2153.127787,\\\"endTime\\\":2153.236907,\\\"responseReceivedTime\\\":2153.188976,\\\"transferSize\\\":4740},\\\"children\\\":{}},\\\"2047.3\\\":{\\\"request\\\":{\\\"url\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\",\\\"startTime\\\":2153.128558,\\\"endTime\\\":2153.473274,\\\"responseReceivedTime\\\":2153.292108,\\\"transferSize\\\":20538},\\\"children\\\":{}}},\\\"longestChain\\\":{\\\"duration\\\":1005.8429999999134,\\\"length\\\":1,\\\"transferSize\\\":20538}}},\\\"score\\\":100},{\\\"id\\\":\\\"user-timings\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"0\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"user-timings\\\",\\\"description\\\":\\\"User Timing marks and measures\\\",\\\"helpText\\\":\\\"Consider instrumenting your app with the User Timing API to create custom, real-world measurements of key user experiences. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/user-timing).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Name\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Type\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"bootup-time\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"400 ms\\\",\\\"rawValue\\\":398.2899999995716,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"https://www.google-analytics.com/analytics.js\\\":{\\\"Script Evaluation\\\":88.33199999993667,\\\"Script Parsing & Compile\\\":5.565000000409782},\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\":{\\\"Script Evaluation\\\":80.06300000008196,\\\"Script Parsing & Compile\\\":4.246999999973923,\\\"Style & Layout\\\":2.6689999997615814},\\\"https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.dLR0UQgpDEo.O/m=auth2/rt=j/sv=1/d=1/ed=1/am=AQE/rs=AGLTcCMdWrzahDTQIubih7dySWJqcBU_nw/cb=gapi.loaded_0\\\":{\\\"Script Evaluation\\\":73.78999999910593,\\\"Script Parsing & Compile\\\":3.5409999997355044,\\\"Style & Layout\\\":1.125},\\\"https://pwa-directory.appspot.com/\\\":{\\\"Parsing HTML & CSS\\\":56.77900000009686},\\\"https://accounts.google.com/o/oauth2/iframe\\\":{\\\"Script Evaluation\\\":29.386999999638647,\\\"Script Parsing & Compile\\\":0.12700000032782555},\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\":{\\\"Script Evaluation\\\":19.56899999966845,\\\"Script Parsing & Compile\\\":6.076000000350177,\\\"Style & Layout\\\":0.04199999989941716},\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\":{\\\"Script Evaluation\\\":6.666000000201166,\\\"Script Parsing & Compile\\\":5.13799999980256},\\\"https://accounts.google.com/o/oauth2/iframe#origin=https%3A%2F%2Fpwa-directory.appspot.com&rpcToken=401330920.1489836&clearCache=1\\\":{\\\"Parsing HTML & CSS\\\":7.239000000059605},\\\"https://accounts.google.com/o/oauth2/iframerpc?action=checkOrigin&origin=https%3A%2F%2Fpwa-directory.appspot.com&client_id=896499748108-ru4bhfh743clb3v3dc8bgf1v68umkvcu.apps.googleusercontent.com\\\":{\\\"Script Evaluation\\\":7.126000000629574},\\\"https://pwa-directory.appspot.com/sw.js\\\":{\\\"Parsing HTML & CSS\\\":0.8089999998919666}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"bootup-time\\\",\\\"description\\\":\\\"JavaScript boot-up time\\\",\\\"helpText\\\":\\\"Consider reducing the time spent parsing, compiling and executing JS. You may find delivering smaller JS payloads helps with this.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Script Parsing & Compile\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://www.google-analytics.com/analytics.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"88 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://pwa-directory.appspot.com/js/gulliver.cd85edbda4.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"80 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/_/scs/apps-static/_/js/k=oz.gapi.en_US.dLR0UQgpDEo.O/m=auth2/rt=j/sv=1/d=1/ed=1/am=AQE/rs=AGLTcCMdWrzahDTQIubih7dySWJqcBU_nw/cb=gapi.loaded_0\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"74 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"4 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://accounts.google.com/o/oauth2/iframe\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"29 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://apis.google.com/js/api.js?onload=gapiResolve\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"20 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"6 ms\\\"}],[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"https://ssl.gstatic.com/accounts/o/2818585737-idpiframe.js\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 ms\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"5 ms\\\"}]]}},\\\"score\\\":100},{\\\"id\\\":\\\"screenshot-thumbnails\\\",\\\"weight\\\":0,\\\"result\\\":{\\\"score\\\":100,\\\"displayValue\\\":\\\"true\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"screenshot-thumbnails\\\",\\\"description\\\":\\\"Screenshot Thumbnails\\\",\\\"helpText\\\":\\\"This is what the load of your site looked like.\\\",\\\"details\\\":{\\\"type\\\":\\\"filmstrip\\\",\\\"scale\\\":2898,\\\"items\\\":[{\\\"timing\\\":290,\\\"timestamp\\\":2152753402,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":580,\\\"timestamp\\\":2153043202,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":869,\\\"timestamp\\\":2153333002,\\\"data\\\":\\\"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAMCAgMCAgMDAwMEAwMEBQgFBQQEBQoHBwYIDAoMDAsKCwsNDhIQDQ4RDgsLEBYQERMUFRUVDA8XGBYUGBIUFRQBAwQEBQQFCQUFCRQNCw0UFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFP/AABEIANUAeAMBEQACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AP1ToAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgD//Z\\\"},{\\\"timing\\\":1159,\\\"timestamp\\\":2153622802,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":1449,\\\"timestamp\\\":2153912602,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":1739,\\\"timestamp\\\":2154202402,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2029,\\\"timestamp\\\":2154492202,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2318,\\\"timestamp\\\":2154782002,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2608,\\\"timestamp\\\":2155071802,\\\"data\\\":\\\"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\\\"},{\\\"timing\\\":2898,\\\"timestamp\\\":2155361602,\\\"data\\\":\\\"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\\\"}]}},\\\"score\\\":100},{\\\"id\\\":\\\"mainthread-work-breakdown\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"1,050 ms\\\",\\\"rawValue\\\":1046.6150000002235,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"Evaluate Script\\\":303.1429999987595,\\\"DOM GC\\\":203.60300000058487,\\\"Layout\\\":169.78400000045076,\\\"Parse HTML\\\":65.91100000031292,\\\"Recalculate Style\\\":58.389999999664724,\\\"Paint\\\":54.391999998129904,\\\"Minor GC\\\":37.812999999616295,\\\"Composite Layers\\\":30.600000000093132,\\\"Run Microtasks\\\":30.269000000786036,\\\"Major GC\\\":30.259999999776483,\\\"Compile Script\\\":25.316000001039356,\\\"Update Layer Tree\\\":19.307000000029802,\\\"Parse Stylesheet\\\":10.522000000346452,\\\"XHR Ready State Change\\\":7.12000000057742,\\\"Image Decode\\\":0.1790000000037253,\\\"XHR Load\\\":0.006000000052154064}},\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"name\\\":\\\"mainthread-work-breakdown\\\",\\\"description\\\":\\\"Main thread work breakdown\\\",\\\"helpText\\\":\\\"Consider reducing the time spent parsing, compiling and executing JS.You may find delivering smaller JS payloads helps with this.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Category\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Work\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Time spent\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Evaluate Script\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"303 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Run Microtasks\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"XHR Ready State Change\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"7 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Evaluation\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"XHR Load\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"DOM GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"204 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Minor GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"38 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Garbage collection\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Major GC\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"30 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Style & Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"170 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Style & Layout\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Recalculate Style\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"58 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parsing HTML & CSS\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parse HTML\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"66 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parsing HTML & CSS\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Parse Stylesheet\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"11 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Paint\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Paint\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"54 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compositing\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Composite Layers\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"31 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compositing\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Update Layer Tree\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"19 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Script Parsing & Compile\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Compile Script\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"25 ms\\\"}],[{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Images\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Image Decode\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"0 ms\\\"}]]}},\\\"score\\\":100},{\\\"id\\\":\\\"font-display\\\",\\\"weight\\\":0,\\\"group\\\":\\\"perf-info\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"font-display\\\",\\\"description\\\":\\\"All text remains visible during webfont loads\\\",\\\"helpText\\\":\\\"Leverage the font-display CSS feature to ensure text is user-visible while webfonts are loading. [Learn more](https://developers.google.com/web/updates/2016/02/font-display).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Font URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Font download time\\\"}],\\\"items\\\":[]}},\\\"score\\\":100}],\\\"id\\\":\\\"performance\\\",\\\"score\\\":41.11764705882353},{\\\"name\\\":\\\"Progressive Web App\\\",\\\"weight\\\":1,\\\"description\\\":\\\"These checks validate the aspects of a Progressive Web App, as specified by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist).\\\",\\\"audits\\\":[{\\\"id\\\":\\\"service-worker\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"service-worker\\\",\\\"description\\\":\\\"Registers a service worker\\\",\\\"helpText\\\":\\\"The service worker is the technology that enables your app to use many Progressive Web App features, such as offline, add to homescreen, and push notifications. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/registered-service-worker).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"works-offline\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"works-offline\\\",\\\"description\\\":\\\"Responds with a 200 when offline\\\",\\\"helpText\\\":\\\"If you're building a Progressive Web App, consider using a service worker so that your app can work offline. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-200-when-offline).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"without-javascript\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"without-javascript\\\",\\\"description\\\":\\\"Contains some content when JavaScript is not available\\\",\\\"helpText\\\":\\\"Your app should display some content when JavaScript is disabled, even if it's just a warning to the user that JavaScript is required to use the app. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/no-js).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"is-on-https\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"is-on-https\\\",\\\"description\\\":\\\"Uses HTTPS\\\",\\\"helpText\\\":\\\"All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Insecure URLs:\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"redirects-http\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"redirects-http\\\",\\\"description\\\":\\\"Redirects HTTP traffic to HTTPS\\\",\\\"helpText\\\":\\\"If you've already set up HTTPS, make sure that you redirect all HTTP traffic to HTTPS. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http-redirects-to-https).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"load-fast-enough-for-pwa\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":null,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":null,\\\"error\\\":true,\\\"debugString\\\":\\\"Your page took too long to load. Please follow the opportunities in the report to reduce your page load time, and then try re-running Lighthouse. (NO_FCPUI_IDLE_PERIOD)\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"load-fast-enough-for-pwa\\\",\\\"description\\\":\\\"Page load is not fast enough on 3G\\\",\\\"helpText\\\":\\\"A fast page load over a 3G network ensures a good mobile user experience. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/fast-3g).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"webapp-install-banner\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"warnings\\\":[],\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"webapp-install-banner\\\",\\\"description\\\":\\\"User can be prompted to Install the Web App\\\",\\\"helpText\\\":\\\"Browsers can proactively prompt users to add your app to their homescreen, which can lead to higher engagement. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/install-prompt).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"splash-screen\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]}}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"splash-screen\\\",\\\"description\\\":\\\"Configured for a custom splash screen\\\",\\\"helpText\\\":\\\"A themed splash screen ensures a high-quality experience when users launch your app from their homescreens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/custom-splash-screen).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"themed-omnibox\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"failures\\\":[],\\\"manifestValues\\\":{\\\"isParseFailure\\\":false,\\\"allChecks\\\":[{\\\"id\\\":\\\"hasStartUrl\\\",\\\"failureText\\\":\\\"Manifest does not contain a `start_url`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast192px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 192px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasIconsAtLeast512px\\\",\\\"failureText\\\":\\\"Manifest does not have icons at least 512px\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasPWADisplayValue\\\",\\\"failureText\\\":\\\"Manifest's `display` value is not one of: minimal-ui | fullscreen | standalone\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasBackgroundColor\\\",\\\"failureText\\\":\\\"Manifest does not have `background_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasThemeColor\\\",\\\"failureText\\\":\\\"Manifest does not have `theme_color`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasShortName\\\",\\\"failureText\\\":\\\"Manifest does not have `short_name`\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"shortNameLength\\\",\\\"failureText\\\":\\\"Manifest `short_name` will be truncated when displayed on the homescreen\\\",\\\"passing\\\":true},{\\\"id\\\":\\\"hasName\\\",\\\"failureText\\\":\\\"Manifest does not have `name`\\\",\\\"passing\\\":true}]},\\\"themeColor\\\":\\\"#7cc0ff\\\"}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"themed-omnibox\\\",\\\"description\\\":\\\"Address bar matches brand colors\\\",\\\"helpText\\\":\\\"The browser address bar can be themed to match your site. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/address-bar).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"viewport\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"viewport\\\",\\\"description\\\":\\\"Has a `<meta name=\\\\\\\"viewport\\\\\\\">` tag with `width` or `initial-scale`\\\",\\\"helpText\\\":\\\"Add a viewport meta tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"content-width\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"content-width\\\",\\\"description\\\":\\\"Content is sized correctly for the viewport\\\",\\\"helpText\\\":\\\"If the width of your app's content doesn't match the width of the viewport, your app might not be optimized for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/content-sized-correctly-for-viewport).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"pwa-cross-browser\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-pwa-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-cross-browser\\\",\\\"description\\\":\\\"Site works cross-browser\\\",\\\"helpText\\\":\\\"To reach the most number of users, sites should work across every major browser. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#site-works-cross-browser).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"pwa-page-transitions\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-pwa-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-page-transitions\\\",\\\"description\\\":\\\"Page transitions don't feel like they block on the network\\\",\\\"helpText\\\":\\\"Transitions should feel snappy as you tap around, even on a slow network, a key to perceived performance. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#page-transitions-dont-feel-like-they-block-on-the-network).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"pwa-each-page-has-url\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-pwa-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"pwa-each-page-has-url\\\",\\\"description\\\":\\\"Each page has a URL\\\",\\\"helpText\\\":\\\"Ensure individual pages are deep linkable via the URLs and that URLs are unique for the purpose of shareability on social media. [Learn more](https://developers.google.com/web/progressive-web-apps/checklist#each-page-has-a-url).\\\"},\\\"score\\\":0}],\\\"id\\\":\\\"pwa\\\",\\\"score\\\":90.9090909090909},{\\\"name\\\":\\\"Accessibility\\\",\\\"description\\\":\\\"These checks highlight opportunities to [improve the accessibility of your web app](https://developers.google.com/web/fundamentals/accessibility). Only a subset of accessibility issues can be automatically detected so manual testing is also encouraged.\\\",\\\"audits\\\":[{\\\"id\\\":\\\"accesskeys\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"accesskeys\\\",\\\"description\\\":\\\"`[accesskey]` values are not unique\\\",\\\"helpText\\\":\\\"Access keys let users quickly focus a part of the page. For proper navigation, each access key must be unique. [Learn more](https://dequeuniversity.com/rules/axe/2.2/accesskeys?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-allowed-attr\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-allowed-attr\\\",\\\"description\\\":\\\"`[aria-*]` attributes do not match their roles\\\",\\\"helpText\\\":\\\"Each ARIA `role` supports a specific subset of `aria-*` attributes. Mismatching these invalidates the `aria-*` attributes. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-allowed-attr?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-required-attr\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-attr\\\",\\\"description\\\":\\\"`[role]`s do not have all required `[aria-*]` attributes\\\",\\\"helpText\\\":\\\"Some ARIA roles have required attributes that describe the state of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-attr?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-required-children\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-children\\\",\\\"description\\\":\\\"Elements with `[role]` that require specific children `[role]`s, are missing.\\\",\\\"helpText\\\":\\\"Some ARIA parent roles must contain specific child roles to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-children?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-required-parent\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-required-parent\\\",\\\"description\\\":\\\"`[role]`s are not contained by their required parent element\\\",\\\"helpText\\\":\\\"Some ARIA child roles must be contained by specific parent roles to properly perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-parent?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-roles\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-roles\\\",\\\"description\\\":\\\"`[role]` values are not valid\\\",\\\"helpText\\\":\\\"ARIA roles must have valid values in order to perform their intended accessibility functions. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-roles?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-valid-attr-value\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-valid-attr-value\\\",\\\"description\\\":\\\"`[aria-*]` attributes do not have valid values\\\",\\\"helpText\\\":\\\"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid values. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr-value?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"aria-valid-attr\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-aria\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"aria-valid-attr\\\",\\\"description\\\":\\\"`[aria-*]` attributes are not valid or misspelled\\\",\\\"helpText\\\":\\\"Assistive technologies, like screen readers, can't interpret ARIA attributes with invalid names. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"audio-caption\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"audio-caption\\\",\\\"description\\\":\\\"`<audio>` elements are missing a `<track>` element with `[kind=\\\\\\\"captions\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"Captions make audio elements usable for deaf or hearing-impaired users, providing critical information such as who is talking, what they're saying, and other non-speech information. [Learn more](https://dequeuniversity.com/rules/axe/2.2/audio-caption?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"button-name\\\",\\\"weight\\\":10,\\\"group\\\":\\\"a11y-element-names\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"button-name\\\",\\\"impact\\\":\\\"critical\\\",\\\"tags\\\":[\\\"cat.name-role-value\\\",\\\"wcag2a\\\",\\\"wcag412\\\",\\\"section508\\\",\\\"section508.22.a\\\"],\\\"description\\\":\\\"Ensures buttons have discernible text\\\",\\\"help\\\":\\\"Buttons must have discernible text\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/button-name?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"critical\\\",\\\"html\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#auth-button\\\"],\\\"failureSummary\\\":\\\"Fix all of the following:\\\\n  Element is in tab order and does not have accessible text\\\\n\\\\nFix any of the following:\\\\n  Element has a value attribute and the value attribute is empty\\\\n  Element has no value attribute or the value attribute is empty\\\\n  Element does not have inner text that is visible to screen readers\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Element's default semantics were not overridden with role=\\\\\\\"presentation\\\\\\\"\\\\n  Element's default semantics were not overridden with role=\\\\\\\"none\\\\\\\"\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,1,BUTTON\\\",\\\"snippet\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"button-name\\\",\\\"description\\\":\\\"Buttons do not have an accessible name\\\",\\\"helpText\\\":\\\"When a button doesn't have an accessible name, screen readers announce it as \\\\\\\"button\\\\\\\", making it unusable for users who rely on screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/button-name?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#auth-button\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,1,BUTTON\\\",\\\"snippet\\\":\\\"<button id=\\\\\\\"auth-button\\\\\\\" class=\\\\\\\"button blue offline-aware\\\\\\\">\\\"}]}},\\\"score\\\":0},{\\\"id\\\":\\\"bypass\\\",\\\"weight\\\":10,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"bypass\\\",\\\"description\\\":\\\"The page contains a heading, skip link, or landmark region\\\",\\\"helpText\\\":\\\"Adding ways to bypass repetitive content lets keyboard users navigate the page more efficiently. [Learn more](https://dequeuniversity.com/rules/axe/2.2/bypass?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"color-contrast\\\",\\\"weight\\\":6,\\\"group\\\":\\\"a11y-color-contrast\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"color-contrast\\\",\\\"impact\\\":\\\"serious\\\",\\\"tags\\\":[\\\"cat.color\\\",\\\"wcag2aa\\\",\\\"wcag143\\\"],\\\"description\\\":\\\"Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds\\\",\\\"help\\\":\\\"Elements must have sufficient color contrast\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/color-contrast?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">New</a>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#newest\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.12 (foreground color: #ffffff, background color: #2196f3, font size: 12.0pt, font weight: normal). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">\\\"},{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">SmartCash PWA</div>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#\\\\\\\\35 658560338853888 > div.pwa-name\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.53 (foreground color: #ffffff, background color: #268ce3, font size: 13.5pt, font weight: bold). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,24,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"},{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">APP Universidad de Colima</div>\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#\\\\\\\\36 031687736623104 > div.pwa-name\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  Element has insufficient color contrast of 3.4 (foreground color: #ffffff, background color: #669933, font size: 13.5pt, font weight: bold). Expected contrast ratio of 4.5:1\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,29,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"color-contrast\\\",\\\"description\\\":\\\"Background and foreground colors do not have a sufficient contrast ratio.\\\",\\\"helpText\\\":\\\"Low-contrast text is difficult or impossible for many users to read. [Learn more](https://dequeuniversity.com/rules/axe/2.2/color-contrast?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#newest\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a id=\\\\\\\"newest\\\\\\\" class=\\\\\\\"tab activetab gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"/\\\\\\\">\\\"},{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#\\\\\\\\35 658560338853888 > div.pwa-name\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,24,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"},{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#\\\\\\\\36 031687736623104 > div.pwa-name\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,1,DIV,29,A,1,DIV\\\",\\\"snippet\\\":\\\"<div class=\\\\\\\"pwa-name\\\\\\\">\\\"}]}},\\\"score\\\":0},{\\\"id\\\":\\\"definition-list\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-well-structured\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"definition-list\\\",\\\"description\\\":\\\"`<dl>`'s do not contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` or `<template>` elements.\\\",\\\"helpText\\\":\\\"When definition lists are not properly marked up, screen readers may produce confusing or inaccurate output. [Learn more](https://dequeuniversity.com/rules/axe/2.2/definition-list?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"dlitem\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-well-structured\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"dlitem\\\",\\\"description\\\":\\\"Definition list items are not wrapped in `<dl>` elements\\\",\\\"helpText\\\":\\\"Definition list items (`<dt>` and `<dd>`) must be wrapped in a parent `<dl>` element to ensure that screen readers can properly announce them. [Learn more](https://dequeuniversity.com/rules/axe/2.2/dlitem?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"document-title\\\",\\\"weight\\\":2,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"document-title\\\",\\\"description\\\":\\\"Document has a `<title>` element\\\",\\\"helpText\\\":\\\"Screen reader users use page titles to get an overview of the contents of the page. [Learn more](https://dequeuniversity.com/rules/axe/2.2/document-title?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"duplicate-id\\\",\\\"weight\\\":5,\\\"group\\\":\\\"a11y-well-structured\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"duplicate-id\\\",\\\"description\\\":\\\"`[id]` attributes on the page are unique\\\",\\\"helpText\\\":\\\"The value of an id attribute must be unique to prevent other instances from being overlooked by assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/duplicate-id?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"frame-title\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"frame-title\\\",\\\"description\\\":\\\"`<frame>` or `<iframe>` elements do not have a title\\\",\\\"helpText\\\":\\\"Screen reader users rely on frame titles to describe the contents of frames. [Learn more](https://dequeuniversity.com/rules/axe/2.2/frame-title?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"html-has-lang\\\",\\\"weight\\\":4,\\\"group\\\":\\\"a11y-language\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"html-has-lang\\\",\\\"description\\\":\\\"`<html>` element has a `[lang]` attribute\\\",\\\"helpText\\\":\\\"If a page doesn't specify a lang attribute, a screen reader assumes that the page is in the default language that the user chose when setting up the screen reader. If the page isn't actually in the default language, then the screen reader might not announce the page's text correctly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/html-lang?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"html-lang-valid\\\",\\\"weight\\\":1,\\\"group\\\":\\\"a11y-language\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"html-lang-valid\\\",\\\"description\\\":\\\"`<html>` element has a valid value for its `[lang]` attribute\\\",\\\"helpText\\\":\\\"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) helps screen readers announce text properly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"image-alt\\\",\\\"weight\\\":8,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"image-alt\\\",\\\"description\\\":\\\"Image elements have `[alt]` attributes\\\",\\\"helpText\\\":\\\"Informative elements should aim for short, descriptive alternate text. Decorative elements can be ignored with an empty alt attribute.[Learn more](https://dequeuniversity.com/rules/axe/2.2/image-alt?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"input-image-alt\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"input-image-alt\\\",\\\"description\\\":\\\"`<input type=\\\\\\\"image\\\\\\\">` elements do not have `[alt]` text\\\",\\\"helpText\\\":\\\"When an image is being used as an `<input>` button, providing alternative text can help screen reader users understand the purpose of the button. [Learn more](https://dequeuniversity.com/rules/axe/2.2/input-image-alt?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"label\\\",\\\"weight\\\":10,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"label\\\",\\\"impact\\\":\\\"critical\\\",\\\"tags\\\":[\\\"cat.forms\\\",\\\"wcag2a\\\",\\\"wcag332\\\",\\\"wcag131\\\",\\\"section508\\\",\\\"section508.22.n\\\"],\\\"description\\\":\\\"Ensures every form element has a label\\\",\\\"help\\\":\\\"Form elements must have labels\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/label?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"critical\\\",\\\"html\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\",\\\"element\\\":null,\\\"target\\\":[\\\"#search-input\\\"],\\\"failureSummary\\\":\\\"Fix any of the following:\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Form element does not have an implicit (wrapped) <label>\\\\n  Form element does not have an explicit <label>\\\\n  Element has no title attribute or the title attribute is empty\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,0,FORM,0,DIV,0,INPUT\\\",\\\"snippet\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"label\\\",\\\"description\\\":\\\"Form elements do not have associated labels\\\",\\\"helpText\\\":\\\"Labels ensure that form controls are announced properly by assistive technologies, like screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/label?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\"#search-input\\\",\\\"path\\\":\\\"1,HTML,1,BODY,1,DIV,1,DIV,0,FORM,0,DIV,0,INPUT\\\",\\\"snippet\\\":\\\"<input id=\\\\\\\"search-input\\\\\\\" type=\\\\\\\"text\\\\\\\" name=\\\\\\\"search\\\\\\\" class=\\\\\\\"form-input box-shadow\\\\\\\" placeholder=\\\\\\\"Search for PWAs\\\\\\\">\\\"}]}},\\\"score\\\":0},{\\\"id\\\":\\\"layout-table\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"layout-table\\\",\\\"description\\\":\\\"Presentational `<table>` elements do not avoid using `<th>`, `<caption>` or the `[summary]` attribute.\\\",\\\"helpText\\\":\\\"A table being used for layout purposes should not include data elements, such as the th or caption elements or the summary attribute, because this can create a confusing experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/layout-table?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"link-name\\\",\\\"weight\\\":9,\\\"group\\\":\\\"a11y-element-names\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"id\\\":\\\"link-name\\\",\\\"impact\\\":\\\"serious\\\",\\\"tags\\\":[\\\"cat.name-role-value\\\",\\\"wcag2a\\\",\\\"wcag111\\\",\\\"wcag412\\\",\\\"wcag244\\\",\\\"section508\\\",\\\"section508.22.a\\\"],\\\"description\\\":\\\"Ensures links have discernible text\\\",\\\"help\\\":\\\"Links must have discernible text\\\",\\\"helpUrl\\\":\\\"https://dequeuniversity.com/rules/axe/2.6/link-name?application=axeAPI\\\",\\\"nodes\\\":[{\\\"any\\\":null,\\\"all\\\":null,\\\"none\\\":null,\\\"impact\\\":\\\"serious\\\",\\\"html\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\"><svg fill=\\\\\\\"#1976D2\\\\\\\" height=\\\\\\\"48\\\\\\\" viewBox=\\\\\\\"0 0 24 24\\\\\\\" width=\\\\\\\"48\\\\\\\" xmlns=\\\\\\\"http://www.w3.org/2000/svg\\\\\\\"><path d=\\\\\\\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\\\\\\\"></path><path d=\\\\\\\"M0 0h24v24H0z\\\\\\\" fill=\\\\\\\"none\\\\\\\"></path></svg></a>\\\",\\\"element\\\":null,\\\"target\\\":[\\\".pager > a\\\"],\\\"failureSummary\\\":\\\"Fix all of the following:\\\\n  Element is in tab order and does not have accessible text\\\\n\\\\nFix any of the following:\\\\n  Element does not have text that is visible to screen readers\\\\n  aria-label attribute does not exist or is empty\\\\n  aria-labelledby attribute does not exist, references elements that do not exist or references elements that are empty or not visible\\\\n  Element's default semantics were not overridden with role=\\\\\\\"presentation\\\\\\\"\\\\n  Element's default semantics were not overridden with role=\\\\\\\"none\\\\\\\"\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\">\\\"}]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"link-name\\\",\\\"description\\\":\\\"Links do not have a discernible name\\\",\\\"helpText\\\":\\\"Link text (and alternate text for images, when used as links) that is discernible, unique, and focusable improves the navigation experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/link-name?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[{\\\"type\\\":\\\"node\\\",\\\"selector\\\":\\\".pager > a\\\",\\\"path\\\":\\\"1,HTML,1,BODY,2,DIV,0,MAIN,2,DIV,0,A\\\",\\\"snippet\\\":\\\"<a class=\\\\\\\"next gulliver-content-only offline-aware\\\\\\\" href=\\\\\\\"?page=2\\\\\\\">\\\"}]}},\\\"score\\\":0},{\\\"id\\\":\\\"list\\\",\\\"weight\\\":5,\\\"group\\\":\\\"a11y-well-structured\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"list\\\",\\\"description\\\":\\\"Lists contain only `<li>` elements and script supporting elements (`<script>` and `<template>`).\\\",\\\"helpText\\\":\\\"Screen readers have a specific way of announcing lists. Ensuring proper list structure aids screen reader output. [Learn more](https://dequeuniversity.com/rules/axe/2.2/list?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"listitem\\\",\\\"weight\\\":4,\\\"group\\\":\\\"a11y-well-structured\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"listitem\\\",\\\"description\\\":\\\"List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements\\\",\\\"helpText\\\":\\\"Screen readers require list items (`<li>`) to be contained within a parent `<ul>` or `<ol>` to be announced properly. [Learn more](https://dequeuniversity.com/rules/axe/2.2/listitem?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"meta-refresh\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-meta\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"meta-refresh\\\",\\\"description\\\":\\\"The document uses `<meta http-equiv=\\\\\\\"refresh\\\\\\\">`\\\",\\\"helpText\\\":\\\"Users do not expect a page to refresh automatically, and doing so will move focus back to the top of the page. This may create a frustrating or confusing experience. [Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-refresh?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"meta-viewport\\\",\\\"weight\\\":3,\\\"group\\\":\\\"a11y-meta\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"meta-viewport\\\",\\\"description\\\":\\\"`[user-scalable=\\\\\\\"no\\\\\\\"]` is not used in the `<meta name=\\\\\\\"viewport\\\\\\\">` element and the `[maximum-scale]` attribute is not less than 5.\\\",\\\"helpText\\\":\\\"Disabling zooming is problematic for users with low vision who rely on screen magnification to properly see the contents of a web page. [Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-viewport?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"object-alt\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"object-alt\\\",\\\"description\\\":\\\"`<object>` elements do not have `[alt]` text\\\",\\\"helpText\\\":\\\"Screen readers cannot translate non-text content. Adding alt text to `<object>` elements helps screen readers convey meaning to users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/object-alt?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"tabindex\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"tabindex\\\",\\\"description\\\":\\\"Some elements have a `[tabindex]` value greater than 0\\\",\\\"helpText\\\":\\\"A value greater than 0 implies an explicit navigation ordering. Although technically valid, this often creates frustrating experiences for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/tabindex?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"td-headers-attr\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"td-headers-attr\\\",\\\"description\\\":\\\"Cells in a `<table>` element that use the `[headers]` attribute refers to other cells of that same table.\\\",\\\"helpText\\\":\\\"Screen readers have features to make navigating tables easier. Ensuring `<td>` cells using the `[headers]` attribute only refer to other cells in the same table may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/td-headers-attr?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"th-has-data-cells\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-correct-attributes\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"th-has-data-cells\\\",\\\"description\\\":\\\"`<th>` elements and elements with `[role=\\\\\\\"columnheader\\\\\\\"/\\\\\\\"rowheader\\\\\\\"]` do not have data cells they describe.\\\",\\\"helpText\\\":\\\"Screen readers have features to make navigating tables easier. Ensuring table headers always refer to some set of cells may improve the experience for screen reader users. [Learn more](https://dequeuniversity.com/rules/axe/2.2/th-has-data-cells?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"valid-lang\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-language\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"valid-lang\\\",\\\"description\\\":\\\"`[lang]` attributes do not have a valid value\\\",\\\"helpText\\\":\\\"Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) on elements helps ensure that text is pronounced correctly by a screen reader. [Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"video-caption\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"video-caption\\\",\\\"description\\\":\\\"`<video>` elements do not contain a `<track>` element with `[kind=\\\\\\\"captions\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"When a video provides a caption it is easier for deaf and hearing impaired users to access its information. [Learn more](https://dequeuniversity.com/rules/axe/2.2/video-caption?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"video-description\\\",\\\"weight\\\":0,\\\"group\\\":\\\"a11y-describe-contents\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"video-description\\\",\\\"description\\\":\\\"`<video>` elements do not contain a `<track>` element with `[kind=\\\\\\\"description\\\\\\\"]`.\\\",\\\"helpText\\\":\\\"Audio descriptions provide relevant information for videos that dialogue cannot, such as facial expressions and scenes. [Learn more](https://dequeuniversity.com/rules/axe/2.2/video-description?application=lighthouse).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"logical-tab-order\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"logical-tab-order\\\",\\\"description\\\":\\\"The page has a logical tab order\\\",\\\"helpText\\\":\\\"Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"focusable-controls\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"focusable-controls\\\",\\\"description\\\":\\\"Interactive controls are keyboard focusable\\\",\\\"helpText\\\":\\\"Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"managed-focus\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"managed-focus\\\",\\\"description\\\":\\\"The user's focus is directed to new content added to the page\\\",\\\"helpText\\\":\\\"If new content, such as a dialog, is added to the page, the user's focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"focus-traps\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"focus-traps\\\",\\\"description\\\":\\\"User focus is not accidentally trapped in a region\\\",\\\"helpText\\\":\\\"A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"custom-controls-labels\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"custom-controls-labels\\\",\\\"description\\\":\\\"Custom controls have associated labels\\\",\\\"helpText\\\":\\\"Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"custom-controls-roles\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"custom-controls-roles\\\",\\\"description\\\":\\\"Custom controls have ARIA roles\\\",\\\"helpText\\\":\\\"Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"visual-order-follows-dom\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"visual-order-follows-dom\\\",\\\"description\\\":\\\"Visual order on the page follows DOM order\\\",\\\"helpText\\\":\\\"DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"offscreen-content-hidden\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"offscreen-content-hidden\\\",\\\"description\\\":\\\"Offscreen content is hidden from assistive technology\\\",\\\"helpText\\\":\\\"Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"heading-levels\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"heading-levels\\\",\\\"description\\\":\\\"Headings don't skip levels\\\",\\\"helpText\\\":\\\"Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"use-landmarks\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-a11y-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"use-landmarks\\\",\\\"description\\\":\\\"HTML5 landmark elements are used to improve navigation\\\",\\\"helpText\\\":\\\"Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).\\\"},\\\"score\\\":0}],\\\"id\\\":\\\"accessibility\\\",\\\"score\\\":54.54545454545455},{\\\"name\\\":\\\"Best Practices\\\",\\\"description\\\":\\\"We've compiled some recommendations for modernizing your web app and avoiding performance pitfalls.\\\",\\\"audits\\\":[{\\\"id\\\":\\\"appcache-manifest\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"appcache-manifest\\\",\\\"description\\\":\\\"Avoids Application Cache\\\",\\\"helpText\\\":\\\"Application Cache is deprecated. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/appcache).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"no-websql\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-websql\\\",\\\"description\\\":\\\"Avoids WebSQL DB\\\",\\\"helpText\\\":\\\"Web SQL is deprecated. Consider using IndexedDB instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/web-sql).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"is-on-https\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"is-on-https\\\",\\\"description\\\":\\\"Uses HTTPS\\\",\\\"helpText\\\":\\\"All sites should be protected with HTTPS, even ones that don't handle sensitive data. HTTPS prevents intruders from tampering with or passively listening in on the communications between your app and your users, and is a prerequisite for HTTP/2 and many new web platform APIs. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/https).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Insecure URLs:\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-http2\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"uses-http2\\\",\\\"description\\\":\\\"Uses HTTP/2 for its own resources\\\",\\\"helpText\\\":\\\"HTTP/2 offers many benefits over HTTP/1.1, including binary headers, multiplexing, and server push. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/http2).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Protocol\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"uses-passive-event-listeners\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"uses-passive-event-listeners\\\",\\\"description\\\":\\\"Uses passive listeners to improve scrolling performance\\\",\\\"helpText\\\":\\\"Consider marking your touch and wheel event listeners as `passive` to improve your page's scroll performance. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/passive-event-listeners).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"no-mutation-events\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":{\\\"results\\\":[]}},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-mutation-events\\\",\\\"description\\\":\\\"Avoids Mutation Events in its own scripts\\\",\\\"helpText\\\":\\\"Mutation Events are deprecated and harm performance. Consider using Mutation Observers instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/mutation-events).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Event\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Line\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Col\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Snippet\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"no-document-write\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-document-write\\\",\\\"description\\\":\\\"Avoids `document.write()`\\\",\\\"helpText\\\":\\\"For users on slow connections, external scripts dynamically injected via `document.write()` can delay page load by tens of seconds. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/document-write).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"external-anchors-use-rel-noopener\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"external-anchors-use-rel-noopener\\\",\\\"description\\\":\\\"Opens external anchors using `rel=\\\\\\\"noopener\\\\\\\"`\\\",\\\"helpText\\\":\\\"Open new tabs using `rel=\\\\\\\"noopener\\\\\\\"` to improve performance and prevent security vulnerabilities. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/noopener).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Target\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Rel\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"geolocation-on-start\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"geolocation-on-start\\\",\\\"description\\\":\\\"Avoids requesting the geolocation permission on page load\\\",\\\"helpText\\\":\\\"Users are mistrustful of or confused by sites that request their location without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/geolocation-on-load).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"no-vulnerable-libraries\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"no-vulnerable-libraries\\\",\\\"description\\\":\\\"Avoids front-end JavaScript libraries with known security vulnerabilities\\\",\\\"helpText\\\":\\\"Some third-party scripts may contain known security vulnerabilities  that are easily identified and exploited by attackers.\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"notification-on-start\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"notification-on-start\\\",\\\"description\\\":\\\"Avoids requesting the notification permission on page load\\\",\\\"helpText\\\":\\\"Users are mistrustful of or confused by sites that request to send notifications without context. Consider tying the request to user gestures instead. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/notifications-on-load).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Location\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"deprecations\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"deprecations\\\",\\\"description\\\":\\\"Avoids deprecated APIs\\\",\\\"helpText\\\":\\\"Deprecated APIs will eventually be removed from the browser. [Learn more](https://www.chromestatus.com/features#deprecated).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Deprecation / Warning\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Line\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"manifest-short-name-length\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"manifest-short-name-length\\\",\\\"description\\\":\\\"Manifest's `short_name` won't be truncated when displayed on homescreen\\\",\\\"helpText\\\":\\\"Make your app's `short_name` fewer than 12 characters to ensure that it's not truncated on homescreens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/manifest-short_name-is-not-truncated).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"password-inputs-can-be-pasted-into\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{\\\"value\\\":[]},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"password-inputs-can-be-pasted-into\\\",\\\"description\\\":\\\"Allows users to paste into password fields\\\",\\\"helpText\\\":\\\"Preventing password pasting undermines good security policy. [Learn more](https://www.ncsc.gov.uk/blog-post/let-them-paste-passwords)\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"Password inputs that prevent pasting into\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"errors-in-console\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":0,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"errors-in-console\\\",\\\"description\\\":\\\"No browser errors logged to the console\\\",\\\"helpText\\\":\\\"Errors logged to the console indicate unresolved problems. They can come from network request failures and other browser concerns.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Description\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"image-aspect-ratio\\\",\\\"weight\\\":1,\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"image-aspect-ratio\\\",\\\"description\\\":\\\"Displays images with correct aspect ratio\\\",\\\"helpText\\\":\\\"Image display dimensions should match natural aspect ratio.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"thumbnail\\\",\\\"text\\\":\\\"\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"URL\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Aspect Ratio (Displayed)\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Aspect Ratio (Actual)\\\"}],\\\"items\\\":[]}},\\\"score\\\":100}],\\\"id\\\":\\\"best-practices\\\",\\\"score\\\":100},{\\\"name\\\":\\\"SEO\\\",\\\"description\\\":\\\"These checks ensure that your page is optimized for search engine results ranking. There are additional factors Lighthouse does not check that may affect your search ranking. [Learn more](https://support.google.com/webmasters/answer/35769).\\\",\\\"audits\\\":[{\\\"id\\\":\\\"viewport\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-mobile\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":\\\"\\\",\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"viewport\\\",\\\"description\\\":\\\"Has a `<meta name=\\\\\\\"viewport\\\\\\\">` tag with `width` or `initial-scale`\\\",\\\"helpText\\\":\\\"Add a viewport meta tag to optimize your app for mobile screens. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/has-viewport-meta-tag).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"document-title\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"extendedInfo\\\":{},\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"document-title\\\",\\\"description\\\":\\\"Document has a `<title>` element\\\",\\\"helpText\\\":\\\"Screen reader users use page titles to get an overview of the contents of the page. [Learn more](https://dequeuniversity.com/rules/axe/2.2/document-title?application=lighthouse).\\\",\\\"details\\\":{\\\"type\\\":\\\"list\\\",\\\"header\\\":{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"View failing elements\\\"},\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"meta-description\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"meta-description\\\",\\\"description\\\":\\\"Document has a meta description\\\",\\\"helpText\\\":\\\"Meta descriptions may be included in search results to concisely summarize page content. [Learn more](https://support.google.com/webmasters/answer/35624?hl=en#1).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"http-status-code\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-crawl\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"http-status-code\\\",\\\"description\\\":\\\"Page has successful HTTP status code\\\",\\\"helpText\\\":\\\"Pages with unsuccessful HTTP status codes may not be indexed properly. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/successful-http-code).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"link-text\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"link-text\\\",\\\"description\\\":\\\"Links have descriptive text\\\",\\\"helpText\\\":\\\"Descriptive link text helps search engines understand your content. [Learn more](https://webmasters.googleblog.com/2008/10/importance-of-link-architecture.html).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Link destination\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Link Text\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"is-crawlable\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-crawl\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"is-crawlable\\\",\\\"description\\\":\\\"Page isn’t blocked from indexing\\\",\\\"helpText\\\":\\\"The \\\\\\\"Robots\\\\\\\" directives tell crawlers how your content should be indexed. [Learn more](https://developers.google.com/search/reference/robots_meta_tag).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Source\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"hreflang\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"hreflang\\\",\\\"description\\\":\\\"Document has a valid `hreflang`\\\",\\\"helpText\\\":\\\"hreflang allows crawlers to discover alternate translations of the page content. [Learn more](https://support.google.com/webmasters/answer/189077).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Source\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"canonical\\\",\\\"weight\\\":0,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"notApplicable\\\":true,\\\"name\\\":\\\"canonical\\\",\\\"description\\\":\\\"Document has a valid `rel=canonical`\\\",\\\"helpText\\\":\\\"Canonical links suggest which URL to show in search results. Read more in [Use canonical URLs](https://support.google.com/webmasters/answer/139066).\\\"},\\\"score\\\":100},{\\\"id\\\":\\\"font-size\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-mobile\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"debugString\\\":null,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"font-size\\\",\\\"description\\\":\\\"Document uses legible font sizes\\\",\\\"helpText\\\":\\\"Font sizes less than 16px are too small to be legible and require mobile visitors to “pinch to zoom” in order to read. Strive to have >75% of page text ≥16px. [Learn more](https://developers.google.com/speed/docs/insights/UseLegibleFontSizes).\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"url\\\",\\\"text\\\":\\\"Source\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Selector\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"% of Page Text\\\"},{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"text\\\",\\\"text\\\":\\\"Font Size\\\"}],\\\"items\\\":[[{\\\"type\\\":\\\"url\\\",\\\"text\\\":\\\"Legible text\\\"},{\\\"type\\\":\\\"code\\\",\\\"text\\\":null},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"100.00%\\\"},{\\\"type\\\":\\\"text\\\",\\\"text\\\":\\\"≥ 16px\\\"}]]}},\\\"score\\\":100},{\\\"id\\\":\\\"plugins\\\",\\\"weight\\\":1,\\\"group\\\":\\\"seo-content\\\",\\\"result\\\":{\\\"score\\\":true,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":true,\\\"scoringMode\\\":\\\"binary\\\",\\\"name\\\":\\\"plugins\\\",\\\"description\\\":\\\"Document avoids plugins\\\",\\\"helpText\\\":\\\"Most mobile devices do not support plugins, and many desktop browsers restrict them.\\\",\\\"details\\\":{\\\"type\\\":\\\"table\\\",\\\"header\\\":\\\"View Details\\\",\\\"itemHeaders\\\":[{\\\"type\\\":\\\"text\\\",\\\"itemType\\\":\\\"code\\\",\\\"text\\\":\\\"Element source\\\"}],\\\"items\\\":[]}},\\\"score\\\":100},{\\\"id\\\":\\\"mobile-friendly\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-seo-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"mobile-friendly\\\",\\\"description\\\":\\\"Page is mobile friendly\\\",\\\"helpText\\\":\\\"Take the [Mobile-Friendly Test](https://search.google.com/test/mobile-friendly) to check for audits not covered by Lighthouse, like sizing tap targets appropriately. [Learn more](https://developers.google.com/search/mobile-sites/).\\\"},\\\"score\\\":0},{\\\"id\\\":\\\"structured-data\\\",\\\"weight\\\":0,\\\"group\\\":\\\"manual-seo-checks\\\",\\\"result\\\":{\\\"score\\\":false,\\\"displayValue\\\":\\\"\\\",\\\"rawValue\\\":false,\\\"scoringMode\\\":\\\"binary\\\",\\\"informative\\\":true,\\\"manual\\\":true,\\\"name\\\":\\\"structured-data\\\",\\\"description\\\":\\\"Structured data is valid\\\",\\\"helpText\\\":\\\"Run the [Structured Data Testing Tool](https://search.google.com/structured-data/testing-tool/) and the [Structured Data Linter](http://linter.structured-data.org/) to validate structured data. [Learn more](https://developers.google.com/search/docs/guides/mark-up-content).\\\"},\\\"score\\\":0}],\\\"id\\\":\\\"seo\\\",\\\"score\\\":100}],\\\"reportGroups\\\":{\\\"perf-metric\\\":{\\\"title\\\":\\\"Metrics\\\",\\\"description\\\":\\\"These metrics encapsulate your web app's performance across a number of dimensions.\\\"},\\\"perf-hint\\\":{\\\"title\\\":\\\"Opportunities\\\",\\\"description\\\":\\\"These are opportunities to speed up your application by optimizing the following resources.\\\"},\\\"perf-info\\\":{\\\"title\\\":\\\"Diagnostics\\\",\\\"description\\\":\\\"More information about the performance of your application.\\\"},\\\"a11y-color-contrast\\\":{\\\"title\\\":\\\"Color Contrast Is Satisfactory\\\",\\\"description\\\":\\\"These are opportunities to improve the legibility of your content.\\\"},\\\"a11y-describe-contents\\\":{\\\"title\\\":\\\"Elements Describe Contents Well\\\",\\\"description\\\":\\\"These are opportunities to make your content easier to understand for a user of assistive technology, like a screen reader.\\\"},\\\"a11y-well-structured\\\":{\\\"title\\\":\\\"Elements Are Well Structured\\\",\\\"description\\\":\\\"These are opportunities to make sure your HTML is appropriately structured.\\\"},\\\"a11y-aria\\\":{\\\"title\\\":\\\"ARIA Attributes Follow Best Practices\\\",\\\"description\\\":\\\"These are opportunities to improve the usage of ARIA in your application which may enhance the experience for users of assistive technology, like a screen reader.\\\"},\\\"a11y-correct-attributes\\\":{\\\"title\\\":\\\"Elements Use Attributes Correctly\\\",\\\"description\\\":\\\"These are opportunities to improve the configuration of your HTML elements.\\\"},\\\"a11y-element-names\\\":{\\\"title\\\":\\\"Elements Have Discernible Names\\\",\\\"description\\\":\\\"These are opportunities to improve the semantics of the controls in your application. This may enhance the experience for users of assistive technology, like a screen reader.\\\"},\\\"a11y-language\\\":{\\\"title\\\":\\\"Page Specifies Valid Language\\\",\\\"description\\\":\\\"These are opportunities to improve the interpretation of your content by users in different locales.\\\"},\\\"a11y-meta\\\":{\\\"title\\\":\\\"Meta Tags Used Properly\\\",\\\"description\\\":\\\"These are opportunities to improve the user experience of your site.\\\"},\\\"manual-a11y-checks\\\":{\\\"title\\\":\\\"Additional items to manually check\\\",\\\"description\\\":\\\"These items address areas which an automated testing tool cannot cover. Learn more in our guide on [conducting an accessibility review](https://developers.google.com/web/fundamentals/accessibility/how-to-review).\\\"},\\\"manual-pwa-checks\\\":{\\\"title\\\":\\\"Additional items to manually check\\\",\\\"description\\\":\\\"These checks are required by the baseline [PWA Checklist](https://developers.google.com/web/progressive-web-apps/checklist) but are not automatically checked by Lighthouse. They do not affect your score but it's important that you verify them manually.\\\"},\\\"seo-mobile\\\":{\\\"title\\\":\\\"Mobile Friendly\\\",\\\"description\\\":\\\"Make sure your pages are mobile friendly so users don’t have to pinch or zoom in order to read the content pages. [Learn more](https://developers.google.com/search/mobile-sites/).\\\"},\\\"seo-content\\\":{\\\"title\\\":\\\"Content Best Practices\\\",\\\"description\\\":\\\"Format your HTML in a way that enables crawlers to better understand your app’s content.\\\"},\\\"seo-crawl\\\":{\\\"title\\\":\\\"Crawling and Indexing\\\",\\\"description\\\":\\\"To appear in search results, crawlers need access to your app.\\\"},\\\"manual-seo-checks\\\":{\\\"title\\\":\\\"Additional items to manually check\\\",\\\"description\\\":\\\"Run these additional validators on your site to check additional SEO best practices.\\\"}},\\\"timing\\\":{\\\"total\\\":16527}}\"\n    }\n  }\n]\n"
  },
  {
    "path": "test/app/lib/lighthouse.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n'use strict';\n\nconst lighthouseLib = require('../../../lib/lighthouse');\nconst dataFetcher = require('../../../lib/data-fetcher');\n\nlet simpleMock = require('simple-mock');\nlet chai = require('chai');\nlet chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nlet assert = require('chai').assert;\n\nconst LIGHTHOUSE_JSON_EXAMPLE = './test/app/lib/lighthouse-example.json';\n\ndescribe('lib.lighthouse', () => {\n  it('processLighthouseJson(lighthouse-example.json) should work', () => {\n    return dataFetcher.readFile(LIGHTHOUSE_JSON_EXAMPLE)\n      .then(data => {\n        const rawData = JSON.parse(data)[0].rawData.value;\n        const lighthouseInfo = lighthouseLib.processLighthouseJson(JSON.parse(rawData));\n        assert.equal(lighthouseInfo.totalScore, 91);\n        assert.equal(lighthouseInfo.lighthouseVersion, '2.9.1');\n        assert.equal(lighthouseInfo.reportCategories[1].name, 'Progressive Web App');\n      });\n  });\n\n  it('getLighthouseGraphByPwaId should return null if theres not data for PWA', () => {\n    simpleMock.mock(lighthouseLib, 'getLighthouseByPwaId').resolveWith([]);\n    return lighthouseLib.getLighthouseGraphByPwaId(123).should.be.fulfilled.then(json => {\n      assert.equal(json, null);\n      assert.equal(lighthouseLib.getLighthouseByPwaId.callCount, 1);\n    });\n  });\n\n  let lighthouseData = {};\n  lighthouseData = {\n    date: '2016-10-27',\n    id: '5768151446847488-2016-10-27',\n    totalScore: 69,\n    lighthouseInfo: [],\n    lighthouseVersion: '1.1.6',\n    pwaId: 5768151446847488,\n    absoluteStartUrl: 'https://www.ampproject.org/'\n  };\n\n  it('getLighthouseGraphByPwaId should crete graph formatted data', () => {\n    simpleMock.mock(lighthouseLib, 'getLighthouseByPwaId').resolveWith([lighthouseData]);\n    return lighthouseLib.getLighthouseGraphByPwaId(123).should.be.fulfilled.then(json => {\n      const date = new Date(Date.parse('2016-10-27'));\n      assert.equal(json.cols[0].label, 'Date');\n      assert.equal(json.cols[0].type, 'date');\n      assert.equal(json.cols[1].label, 'Score');\n      assert.equal(json.cols[1].type, 'number');\n      assert.equal(json.rows[0].c[0].v,\n        'Date(' + date.getFullYear() + ',' + date.getMonth() + ',' + date.getDate() + ')');\n      assert.equal(json.rows[0].c[1].v, 69);\n      assert.equal(lighthouseLib.getLighthouseByPwaId.callCount, 1);\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/manifest.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n\nconst fs = require('fs');\n\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nconst assert = require('chai').assert;\n\nchai.use(chaiAsPromised);\nchai.should();\n\nconst libManifest = require('../../../lib/manifest');\n\ndescribe('lib.manifest', () => {\n  describe('#validateManifest', () => {\n    // We assume lighthouse has tests for the manifest parser, just run some\n    // minimal smoke tests here.\n    it('returns empty array if manifest ok', () => {\n      const manifest = fs.readFileSync('./test/app/manifests/icon-url-with-parameter.json');\n      const manifestUrl = 'https://example.com/';\n      const documentUrl = 'https://www.example.com/';\n      const actual = libManifest.validateManifest(manifest, manifestUrl, documentUrl);\n      assert.deepEqual(actual, []);\n    });\n    it('returns same-origin error if documentUrl does not match', () => {\n      const manifest = fs.readFileSync('./test/app/manifests/icon-url-with-parameter.json');\n      const manifestUrl = 'https://example.com/';\n      const documentUrl = 'https://bar.com/';\n      const actual = libManifest.validateManifest(manifest, manifestUrl, documentUrl);\n      assert.deepEqual(actual, ['ERROR: start_url must be same-origin as document']);\n    });\n    it('returns icon error if icon value invalid', () => {\n      const manifest = fs.readFileSync('./test/app/manifests/no-icon-array.json');\n      const manifestUrl = 'https://example.com/';\n      const documentUrl = 'https://bar.com/';\n      const actual = libManifest.validateManifest(manifest, manifestUrl, documentUrl);\n      assert.deepEqual(actual, ['ERROR: \\'icons\\' expected to be an array but is not.']);\n    });\n    it('returns multiple errors if start_url and theme_color invalid', () => {\n      const manifest = fs.readFileSync('./test/app/manifests/invalid-theme-color.json');\n      const manifestUrl = 'https://example.com/';\n      const documentUrl = 'https://bar.com/';\n      const actual = libManifest.validateManifest(manifest, manifestUrl, documentUrl);\n      assert.deepEqual(actual, [\n        'ERROR: start_url must be same-origin as document',\n        'ERROR: color parsing failed.'\n      ]);\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/model-datastore.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it beforeEach */\n'use strict';\n\nconst assert = require('assert');\nconst config = require('../../../config/config');\nconst db = require('../../../lib/model-datastore');\n\nconst datastore = require('@google-cloud/datastore');\nconst ds = datastore({\n  projectId: config.get('GCLOUD_PROJECT')\n});\n\nconst ENTITY_NAME = 'test';\n\nclass TestClass {\n\n}\n\nconst testObject = new TestClass();\ntestObject.array = ['A', 'B', 'C'];\n\nconst DB_OBJECT = {\n  test: 'test',\n  testObject: testObject,\n  testObject2: {\n    innerTestObject: testObject\n  }\n};\n\ndescribe('lib.model-datastore', () => {\n  const skipTests = process.env.TRAVIS;\n  // Skip tests if Running in CI\n  beforeEach(function() {\n    this.timeout(3000);\n    if (skipTests) {\n      this.skip();\n      return;\n    }\n    // Deletes all entities on the 'test' namespace before each test.\n    return new Promise((resolve, reject) => {\n      const q = ds.createQuery(ENTITY_NAME);\n      ds.runQuery(q, (err, entities) => {\n        if (err) {\n          return reject(err);\n        }\n\n        const keys = entities.map(entity => {\n          return entity[datastore.KEY];\n        });\n\n        // Delete counts for 'test'.\n        keys[keys.length] = ds.key(['counts', ENTITY_NAME]);\n        ds.delete(keys, err => {\n          return reject(err);\n        });\n\n        return resolve();\n      });\n    });\n  });\n\n  describe('#update', () => {\n    it('returns correct values after adding a new object', () => {\n      return db.update(ENTITY_NAME, null, DB_OBJECT)\n        .then(saved => {\n          assert.ok(saved.id, 'An ID has been created');\n          assert.equal(saved.test, DB_OBJECT.test, 'The value of the \"test\" field is correct');\n          assert.ok(Array.isArray(saved.testObject.array),\n             'Check if datastore is not modifying arrays');\n        });\n    });\n  });\n\n  describe('#count', () => {\n    beforeEach(function() {\n      if (skipTests) {\n        this.skip();\n        return;\n      }\n    });\n\n    it('counts objects correctly', () => {\n      return db.updateWithCounts(ENTITY_NAME, null, DB_OBJECT)\n        .then(() => {\n          return db.count(ENTITY_NAME)\n            .then(result => {\n              assert.equal(result, 1, 'Counts 1 entities');\n            });\n        });\n    });\n  });\n\n  describe('#find', () => {\n    let objectId;\n\n    beforeEach(function() {\n      if (skipTests) {\n        this.skip();\n        return;\n      }\n      return db.update(ENTITY_NAME, null, DB_OBJECT).then(testObject => {\n        objectId = testObject.id;\n      });\n    });\n\n    it('find an object', () => {\n      return db.read(ENTITY_NAME, objectId)\n        .then(testObject => {\n          assert.equal(objectId, testObject.id, 'Correct ID returned');\n          assert.equal(testObject.test, DB_OBJECT.test);\n        });\n    });\n  });\n\n  describe('#delete', () => {\n    let objectId;\n\n    beforeEach(function() {\n      if (skipTests) {\n        this.skip();\n        return;\n      }\n      return db.update(ENTITY_NAME, null, DB_OBJECT).then(testObject => {\n        objectId = testObject.id;\n      });\n    });\n\n    it('delete an object', () => {\n      return db.delete(ENTITY_NAME, objectId)\n        .then(() => {\n          return db.read(ENTITY_NAME, objectId)\n            .then(entity => {\n              assert.ok(!entity);\n            })\n            .catch(() => { // es-lint-ignore handle-callback-err\n              assert.ok(true);\n            });\n        });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/notifications.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it afterEach */\n\nconst dataFetcher = require('../../../lib/data-fetcher');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nconst assert = require('chai').assert;\nchai.use(chaiAsPromised);\nchai.should();\nconst notificationsLib = require('../../../lib/notifications');\n\ndescribe('lib.notifications', () => {\n  describe('#list', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('Should reject if token is empty', () => {\n      return notificationsLib.list().should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if fetch fails', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch')\n        .rejectWith(new Error());\n      return notificationsLib.list('123').should.be.rejectedWith(Error);\n    });\n\n    it('Should return an ampty list of topics', () => {\n      const emptyListResponse =\n          JSON.parse('{\"connectDate\":\"2016-12-02\",\"application\":\"wp:http://localhost:3' +\n          '000/#EE89C825-C62B-4683-9336-454325D121A9\",\"authorizedEntity\":\"653391209629\",\"connect' +\n          'ionType\":\"WIFI\",\"platform\":\"WEBPUSH\"}');\n      simpleMock.mock(dataFetcher, 'firebaseFetch')\n        .resolveWith(emptyListResponse);\n      return notificationsLib.list('123').should.be.fulfilled\n        .then(topicList => {\n          assert.equal(topicList.length, 0, 'Topic List is empty');\n        });\n    });\n\n    it('Should return the correct list of topics', () => {\n      const successTemplate = JSON.parse('{\"connectDate\":\"2016-12-01\",\"application\":\"wp:http://' +\n      'localhost:3000/#6E162DF4-1899-418F-BC1C-F04A5A4E387B\",\"authorizedEntity\":\"653391209629\",' +\n      '\"rel\":{\"topics\":{\"test2\":{\"addDate\":\"2016-12-01\"},\"test\":{\"addDate\":\"2016-12-01\"}}},\"conn' +\n      'ectionType\":\"WIFI\",\"platform\":\"WEBPUSH\"}');\n      simpleMock.mock(dataFetcher, 'firebaseFetch')\n        .resolveWith(successTemplate);\n      return notificationsLib.list('123').should.be.fulfilled\n        .then(topicList => {\n          assert.equal(topicList.length, 2, 'Returns correct number of topics');\n          assert(topicList.indexOf('test') >= 0, 'Contains \"test\" topic');\n          assert(topicList.indexOf('test2') >= 0, 'Contains \"test2\" topic');\n        });\n    });\n  });\n\n  describe('#subscribe', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('Should reject if token is empty', () => {\n      return notificationsLib.subscribe().should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if topic is empty', () => {\n      return notificationsLib.subscribe('123').should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if datafetcher fails', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch')\n        .rejectWith(new Error());\n      return notificationsLib.subscribe('123', 'test').should.be.rejectedWith(Error);\n    });\n\n    it('Should resolve if datafetch succeeds', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch')\n        .resolveWith(true);\n      return notificationsLib.subscribe('123', 'test').should.be.fulfilled;\n    });\n  });\n\n  describe('#unsubscribe', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('Should reject if token is empty', () => {\n      return notificationsLib.unsubscribe().should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if topic is empty', () => {\n      return notificationsLib.unsubscribe('123').should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if datafetcher fails', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch').rejectWith(new Error());\n      return notificationsLib.unsubscribe('123', 'test').should.be.rejectedWith(Error);\n    });\n\n    it('Should resolve if datafetch succeeds', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch').resolveWith(true);\n      return notificationsLib.unsubscribe('123', 'test').should.be.fulfilled;\n    });\n  });\n\n  describe('#sendPush', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n\n    it('Should reject if topic is empty', () => {\n      return notificationsLib.sendPush(null, {title: 'Test'}).should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if notification is empty', () => {\n      return notificationsLib.sendPush('test').should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if missing title', () => {\n      return notificationsLib.sendPush('test', {}).should.be.rejectedWith(Error);\n    });\n\n    it('Should reject if datafetcher fails', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch').rejectWith(new Error());\n      return notificationsLib.sendPush('test', {title: 'Test Title'}).should.be.rejectedWith(Error);\n    });\n\n    it('Should resolve if datafetcher succeeds', () => {\n      simpleMock.mock(dataFetcher, 'firebaseFetch').resolveWith(true);\n      return notificationsLib.sendPush('test', {title: 'Test Title'}).should.be.fullfilled;\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/promise-sequential.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n'use strict';\n\nconst promiseSequential = require('../../../lib/promise-sequential');\n\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nconst assert = require('chai').assert;\n\ndescribe('lib.promise-sequential', () => {\n  describe('#all', () => {\n    it('executes all functions on the list', () => {\n      const promiseFunctions = [\n        _ => 1,\n        result => result + 1,\n        result => result + 2,\n        result => result + 3\n      ];\n      return promiseSequential.all(promiseFunctions).should.be.fulfilled.then(result => {\n        assert.equal(result, 7);\n      });\n    });\n  });\n\n  describe('#all', () => {\n    it('executes all functions on the list without parameters', () => {\n      function test(result) {\n        result += 1;\n        return result;\n      }\n      const promiseFunctions = [\n        _ => (10),\n        test,\n        test,\n        test,\n        test\n      ];\n      return promiseSequential.all(promiseFunctions).should.be.fulfilled.then(result => {\n        assert.equal(result, 14);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it afterEach*/\n'use strict';\n\nconst fs = require('fs');\nconst dataFetcher = require('../../../lib/data-fetcher');\nconst libPwa = require('../../../lib/pwa');\nconst libImages = require('../../../lib/images');\nconst libManifest = require('../../../lib/manifest');\nconst libWebPerformance = require('../../../lib/web-performance');\n\nconst Pwa = require('../../../models/pwa');\n\nconst testPwa = require('../models/pwa');\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nconst assert = require('chai').assert;\n\nconst MANIFEST_URL = 'https://www.domain.com/manifest-br.json';\nconst START_URL = 'https://www.domain.com/?utm_source=homescreen';\nconst LIGHTHOUSE_JSON_EXAMPLE = './test/app/lib/lighthouse-example.json';\n\n/* eslint-disable camelcase */\nconst MANIFEST_DATA = {\n  name: 'Test',\n  icons: [\n    {\n      src: 'img/launcher-icon.png?v2',\n      sizes: '192x192',\n      type: 'image/png'\n    }\n  ],\n  start_url: 'https://www.example.com/?utm_source=homescreen'\n};\nconst MANIFEST_NO_ICON = {name: 'Test', description: 'Manifest without icons', start_url: '/'};\nconst MANIFEST_INVALID_THEME_COLOR = {\n  description: 'Manifest with an invalid theme_color', theme_color: ''};\n/* eslint-enable camelcase */\n\ndescribe('lib.pwa', () => {\n  const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n  pwa.id = '123456789';\n  const manifest = pwa.manifest;\n  const pwaNoIcon = testPwa.newPwa(MANIFEST_URL, MANIFEST_NO_ICON);\n  const pwaInvalidThemeColor = testPwa.newPwa(MANIFEST_URL, MANIFEST_INVALID_THEME_COLOR);\n\n  describe('#updatePwaMetadataDescription', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('sets Metadata Description', () => {\n      simpleMock.mock(dataFetcher, 'fetchMetadataDescription').resolveWith('a description');\n      return libPwa.updatePwaMetadataDescription(pwa).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(dataFetcher.fetchMetadataDescription.callCount, 1);\n        assert.equal(updatedPwa.metaDescription, 'a description');\n      });\n    });\n    it('sets Metadata Description, works without metaDescription returned by dataFetcher', () => {\n      simpleMock.mock(dataFetcher, 'fetchMetadataDescription').resolveWith(null);\n      return libPwa.updatePwaMetadataDescription(pwa).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(dataFetcher.fetchMetadataDescription.callCount, 1);\n        assert.equal(updatedPwa.metaDescription, undefined);\n      });\n    });\n    it('sets Metadata Description, works even if there is an error during at dataFetcher', () => {\n      simpleMock.mock(dataFetcher, 'fetchMetadataDescription').rejectWith(new Error());\n      return libPwa.updatePwaMetadataDescription(pwa).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(dataFetcher.fetchMetadataDescription.callCount, 1);\n        assert.equal(updatedPwa.metaDescription, undefined);\n      });\n    });\n  });\n\n  describe('#updatePwaIcon', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('sets iconUrl', () => {\n      simpleMock.mock(libImages, 'fetchAndSave').resolveWith(['original', '128', '64']);\n      return libPwa.updatePwaIcon(pwa).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(libImages.fetchAndSave.callCount, 1);\n        assert.equal(libImages.fetchAndSave.lastCall.args[0],\n          'https://www.domain.com/img/launcher-icon.png?v2');\n        assert.equal(libImages.fetchAndSave.lastCall.args[1], '123456789.png');\n        assert.equal(updatedPwa.iconUrl, 'original');\n        assert.equal(updatedPwa.iconUrl128, '128');\n        assert.equal(updatedPwa.iconUrl64, '64');\n      });\n    });\n    it('allows PWAs without icon', () => {\n      return libPwa.updatePwaIcon(pwaNoIcon).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(updatedPwa.iconUrl, null);\n      });\n    });\n  });\n\n  describe('#updatePwaLighthouseInfo', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('sets lighthouseScore', () => {\n      simpleMock.mock(libWebPerformance, 'getLighthouseReport').resolveWith(\n        JSON.parse(fs.readFileSync(LIGHTHOUSE_JSON_EXAMPLE)));\n      return libPwa.updatePwaLighthouseInfo(pwa).should.be.fulfilled.then(updatedPwa => {\n        assert.equal(libWebPerformance.getLighthouseReport.callCount, 1);\n        assert.equal(libWebPerformance.getLighthouseReport.lastCall.args[0], pwa);\n        assert.equal(updatedPwa.lighthouseScore, 91);\n      });\n    });\n  });\n\n  describe('#fetchManifest', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('Fetches manifest directly from MANIFEST_URL', () => {\n      simpleMock.mock(libManifest, 'fetchManifest').resolveWith(manifest);\n      return libPwa.fetchManifest(pwa).should.be.fulfilled.then(fetchedManifest => {\n        assert.equal(fetchedManifest, manifest);\n        assert.equal(libManifest.fetchManifest.callCount, 1);\n      });\n    });\n    it('Fails directly and looks for manifest link on START_URL', () => {\n      simpleMock.mock(libManifest, 'fetchManifest').rejectWith(new Error()).resolveWith(manifest);\n      simpleMock.mock(dataFetcher, 'fetchLinkRelManifestUrl').resolveWith(MANIFEST_URL);\n      let PwaWithStartUrl = new Pwa(START_URL, manifest);\n      return libPwa.fetchManifest(PwaWithStartUrl)\n      .should.be.fulfilled.then(fetchedManifest => {\n        assert.equal(fetchedManifest, manifest);\n        assert.equal(PwaWithStartUrl.manifestUrl, MANIFEST_URL);\n        assert.equal(libManifest.fetchManifest.callCount, 2);\n        assert.equal(dataFetcher.fetchLinkRelManifestUrl.callCount, 1);\n      });\n    });\n    it('Fails directly and fails for manifest link on START_URL', () => {\n      simpleMock.mock(libManifest, 'fetchManifest').rejectWith(new Error()).resolveWith(manifest);\n      simpleMock.mock(dataFetcher, 'fetchLinkRelManifestUrl').rejectWith(new Error());\n      return libPwa.fetchManifest(new Pwa(START_URL, manifest))\n      .should.be.rejected.then(_ => {\n        assert.equal(libManifest.fetchManifest.callCount, 1);\n        assert.equal(dataFetcher.fetchLinkRelManifestUrl.callCount, 1);\n      });\n    });\n  });\n\n  describe('#updatePwaManifest', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('performs all the save steps', () => {\n      simpleMock.mock(libPwa, 'fetchManifest').resolveWith(manifest);\n      simpleMock.mock(libPwa, 'findByManifestUrl').resolveWith(pwa);\n      return libPwa.updatePwaManifest(pwa).should.be.fulfilled.then(_ => {\n        assert.equal(libPwa.fetchManifest.callCount, 1);\n        assert.equal(libPwa.findByManifestUrl.callCount, 1);\n      });\n    });\n    it('handles E_MANIFEST_ERROR error', () => {\n      simpleMock.mock(libPwa, 'fetchManifest').resolveWith(manifest);\n      simpleMock.mock(libPwa, 'findByManifestUrl').rejectWith(new Error('Testing error'));\n      return libPwa.updatePwaManifest(pwa).should.be.rejectedWith(libPwa.E_MANIFEST_ERROR);\n    });\n    it('rejects invalid Manifest', () => {\n      simpleMock.mock(libPwa, 'fetchManifest').resolveWith(pwaInvalidThemeColor.manifest);\n      simpleMock.mock(libPwa, 'findByManifestUrl').resolveWith(pwaInvalidThemeColor);\n      return libPwa.updatePwaManifest(pwaInvalidThemeColor).should.be.rejected.then(error => {\n        assert.equal(error, 'Error while validating the manifest: ERROR: color parsing failed.');\n      });\n    });\n  });\n\n  describe('#validatePwa', () => {\n    it('rejects on null pwa', () => {\n      return libPwa.validatePwa(null).should.be.rejected.then(error => {\n        assert.equal(error, libPwa.E_NOT_A_PWA);\n      });\n    });\n    it('rejects if not passed a Pwa object', () => {\n      // The right \"shape\", but not actually a Pwa object\n      const obj = {\n        manifestUrl: 'foo',\n        user: {\n          id: 'bar'\n        }\n      };\n      return libPwa.validatePwa(obj).should.be.rejected.then(error => {\n        assert.equal(error, libPwa.E_NOT_A_PWA);\n      });\n    });\n    it('rejects if passed a Pwa object without a manifestUrl', () => {\n      const pwa = new Pwa();\n      return libPwa.validatePwa(pwa).should.be.rejected.then(error => {\n        assert.equal(error, libPwa.E_MANIFEST_URL_MISSING);\n      });\n    });\n    it('rejects if passed a Pwa object with an invalid manifestUrl', () => {\n      const pwa = new Pwa('not a manifest URL');\n      return libPwa.validatePwa(pwa).should.be.rejected.then(error => {\n        assert.equal(error, libPwa.E_MANIFEST_INVALID_URL);\n      });\n    });\n    it('rejects if passed a Pwa object with an invalid user.id', () => {\n      const pwa = new Pwa('https://example.com/', {user: null});\n      return libPwa.validatePwa(pwa).should.be.rejected.then(error => {\n        assert.equal(error, libPwa.E_MISSING_USER_INFORMATION);\n      });\n    });\n    it('fulfills if passed a valid Pwa objectid', () => {\n      const pwa = new Pwa('https://example.com/');\n      pwa.user = {id: '7777'};\n      return libPwa.validatePwa(pwa).should.be.fulfilled.then(result => {\n        assert.equal(result, pwa);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/search.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n\nconst simpleMock = require('simple-mock');\nconst chai = require('chai');\nconst chaiAsPromised = require('chai-as-promised');\nconst assert = require('chai').assert;\n\nchai.use(chaiAsPromised);\nchai.should();\n\nlet search = require('../../../lib/search');\nconst testPwa = require('../models/pwa');\nconst Manifest = require('../../../models/manifest');\n\nconst MANIFEST_URL = 'https://pwa-directory.appspot.com/manifest.json';\n/* eslint-disable camelcase */\nconst MANIFEST_DATA = {\n  name: 'PWA Directory',\n  short_name: 'PwaDirectory',\n  start_url: '/?utm_source=homescreen'\n};\n/* eslint-enable camelcase */\n\ndescribe('lib.Search', () => {\n  describe('#addPwa', () => {\n    const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n    pwa.id = '789';\n\n    it('Add a check doc properties', () => {\n      simpleMock.mock(search, 'addPwa');\n      return search.addPwa(pwa).should.be.fulfilled.then(doc => {\n        assert.equal(search.addPwa.callCount, 1);\n        assert.equal(doc.id, 789);\n        assert.equal(doc.displayName, 'PWA Directory');\n        assert.equal(doc.urlText, 'pwa directory appspot com');\n        search.removePwa(pwa);\n      });\n    });\n  });\n\n  describe('#search', () => {\n    const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n    pwa.id = '123456789';\n\n    it('Find a PWA', () => {\n      search.addPwa(pwa);\n      simpleMock.mock(search, 'search');\n      return search.search('pwa-directory').should.be.fulfilled.then(searchResult => {\n        assert.equal(search.search.callCount, 1);\n        assert.equal(searchResult.length, 1);\n        assert.equal(searchResult[0].ref, 123456789);\n        assert.ok(searchResult[0].score > 0);\n        search.removePwa(pwa);\n      });\n    });\n\n    it('Not find a PWA', () => {\n      search.addPwa(pwa);\n      simpleMock.mock(search, 'search');\n      return search.search('zzz').should.be.fulfilled.then(searchResult => {\n        assert.equal(search.search.callCount, 1);\n        assert.equal(searchResult.length, 0);\n        search.removePwa(pwa);\n      });\n    });\n  });\n\n  describe('#updatePwa', () => {\n    const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n    pwa.id = '98765';\n\n    it('Update and search for change', () => {\n      search.addPwa(pwa);\n      /* eslint-disable camelcase */\n      const UPDATED_MANIFEST_DATA = {\n        name: 'PWA Directory xxx',\n        short_name: 'PwaDirectory',\n        start_url: '/?utm_source=homescreen'\n      };\n      /* eslint-enable camelcase */\n      pwa.manifest = new Manifest(MANIFEST_URL, UPDATED_MANIFEST_DATA);\n      search.updatePwa(pwa);\n      simpleMock.mock(search, 'search');\n      return search.search('xxx').should.be.fulfilled.then(searchResult => {\n        assert.equal(search.search.callCount, 1);\n        assert.equal(searchResult.length, 1);\n        assert.equal(searchResult[0].ref, 98765);\n        assert.ok(searchResult[0].score > 0);\n        search.removePwa(pwa);\n      });\n    });\n  });\n\n  describe('#removePwa', () => {\n    const pwa = testPwa.newPwa(MANIFEST_URL, MANIFEST_DATA);\n    pwa.id = '98765';\n\n    it('Remove and search for removal', () => {\n      search.addPwa(pwa);\n      search.removePwa(pwa);\n      simpleMock.mock(search, 'search');\n      return search.search('pwa-directory').should.be.fulfilled.then(searchResult => {\n        assert.equal(search.search.callCount, 1);\n        assert.equal(searchResult.length, 0);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/lib/tasks.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it before afterEach */\n'use strict';\n\nlet dataFetcher = require('../../../lib/data-fetcher');\nlet libTasks = require('../../../lib/tasks');\nlet libPwa = require('../../../lib/pwa');\nlet db = require('../../../lib/model-datastore');\nlet Pwa = require('../../../models/pwa');\nlet Task = require('../../../models/task');\nlet Manifest = require('../../../models/manifest');\n\nlet simpleMock = require('simple-mock');\nlet chai = require('chai');\nlet chaiAsPromised = require('chai-as-promised');\nchai.use(chaiAsPromised);\nchai.should();\nlet assert = require('chai').assert;\n\nconst MANIFEST_URL = 'https://www.terra.com.br/manifest-br.json';\nconst MANIFEST_DATA = './test/app/manifests/icon-url-with-parameter.json';\n\ndescribe('lib.tasks', () => {\n  let manifest;\n  let pwa;\n  let task;\n  let dbListResult = {};\n  before(done => {\n    dataFetcher.readFile(MANIFEST_DATA)\n      .then(jsonString => {\n        manifest = new Manifest(MANIFEST_URL, JSON.parse(jsonString));\n        pwa = new Pwa(MANIFEST_URL, manifest);\n        pwa.id = 123456789;\n        task = new Task(123456789);\n        dbListResult.entities = new Array(task);\n        done();\n      });\n  });\n\n  describe('#push', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('push a task', () => {\n      // Mock bd to avoid making real calls\n      simpleMock.mock(db, 'update').resolveWith(task);\n      simpleMock.mock(libTasks, 'db').returnWith(db);\n      return libTasks.push(task).should.be.fulfilled.then(savedTask => {\n        assert.equal(savedTask.pwaId, 123456789);\n        assert.equal(db.update.callCount, 1);\n      });\n    });\n  });\n\n  describe('#pop', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('pop a task', () => {\n      // Mock bd to avoid making real calls\n      simpleMock.mock(db, 'list').resolveWith(dbListResult);\n      simpleMock.mock(db, 'delete').resolveWith(task);\n      simpleMock.mock(libTasks, 'db').returnWith(db);\n      return libTasks.pop(task).should.be.fulfilled.then(savedTask => {\n        assert.equal(savedTask.pwaId, 123456789);\n        assert.equal(db.list.callCount, 1);\n        assert.equal(db.delete.callCount, 1);\n      });\n    });\n  });\n\n  describe('#execute', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('execute a task', () => {\n      const modulePath = require.resolve('../../../lib/pwa');\n      const task = new Task(987654321, modulePath, 'createOrUpdatePwa', 1);\n      simpleMock.mock(libPwa, 'find').resolveWith(pwa);\n      simpleMock.mock(libPwa, 'createOrUpdatePwa').resolveWith(pwa);\n      simpleMock.mock(libTasks, 'push').resolveWith(task);\n      return libTasks.executePwaTask(task).should.be.fulfilled.then(executedTask => {\n        assert.equal(libPwa.find.callCount, 1);\n        assert.equal(libPwa.createOrUpdatePwa.callCount, 1);\n        assert.equal(executedTask.pwaId, 987654321);\n        assert.equal(libTasks.push.callCount, 0);\n        assert.equal(executedTask.retries, 1);\n      });\n    });\n    it('retry a task', () => {\n      const modulePath = require.resolve('../../../lib/pwa');\n      const task = new Task(987654321, modulePath, 'createOrUpdatePwa', 1);\n      simpleMock.mock(libPwa, 'find').resolveWith(pwa);\n      simpleMock.mock(libPwa, 'createOrUpdatePwa').rejectWith(new Error('Retry task'));\n      simpleMock.mock(libTasks, 'push').resolveWith(task);\n      return libTasks.executePwaTask(task).should.be.fulfilled.then(executedTask => {\n        assert.equal(libPwa.find.callCount, 1);\n        assert.equal(libPwa.createOrUpdatePwa.callCount, 1);\n        assert.equal(executedTask.pwaId, 987654321);\n        assert.equal(libTasks.push.callCount, 1);\n        assert.equal(executedTask.retries, 0);\n      });\n    });\n  });\n\n  describe('#popExecute', () => {\n    afterEach(() => {\n      simpleMock.restore();\n    });\n    it('pop and execute a task', () => {\n      simpleMock.mock(libTasks, 'pop').resolveWith(task);\n      simpleMock.mock(libTasks, 'executePwaTask').resolveWith(task);\n      return libTasks.popExecute().should.be.fulfilled.then(savedTask => {\n        assert.equal(savedTask.pwaId, 123456789);\n        assert.equal(libTasks.pop.callCount, 1);\n        assert.equal(libTasks.executePwaTask.callCount, 1);\n      });\n    });\n    it('pop and execute a task (pop is null)', () => {\n      simpleMock.mock(libTasks, 'pop').resolveWith(null);\n      simpleMock.mock(libTasks, 'executePwaTask');\n      return libTasks.popExecute().should.be.fulfilled.then(savedTask => {\n        assert.equal(savedTask, null);\n        assert.equal(libTasks.pop.callCount, 1);\n        assert.equal(libTasks.executePwaTask.callCount, 0);\n      });\n    });\n  });\n});\n"
  },
  {
    "path": "test/app/manifests/icon-url-with-parameter.json",
    "content": "{\n  \"name\": \"Test\",\n  \"icons\": [\n    {\n      \"src\": \"img/launcher-icon.png?v2\",\n      \"sizes\": \"192x192\",\n      \"type\": \"image/png\"\n    }\n  ],\n  \"start_url\": \"https://www.example.com/?utm_source=homescreen\"\n}\n"
  },
  {
    "path": "test/app/manifests/inline-image-large-content.json",
    "content": "{\"short_name\":\"Twitter\",\"name\":\"Twitter\",\"icons\":[{\"src\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMAAAADACAMAAABlApw1AAAAk1BMVEX///8dofJ9yfggo/IeofLF5/wnpvM6rfT7/f8sqPM0q/MkpPLs9/7l9P7Q7P2k2fr3/P+T0vnw+f7g8/2FzfhevPZWufVKtPXz+v604Pt2x/dAsPT9/v+Lz/lwxPdrwvdRt/Wv3vqr3PpjvvbL6vy64vua1vlau/bZ7/2+5PtnwPbd8f1EsvROtvXn9v6e1/lHsvRrYjr1AAAESUlEQVR42u3c2XbaMBQF0HM84xkMmHkmDCFN///r2iRNmzRMVmRj1rr7iRdkX10hCVkyhBBCCCGEEEIIIYQQQgghhBBCCCGEOC9aN419Z7OftOexhzszsgcuP3A6SQ/3o9d0+UUwiU8Eu2ihVtJmg8ctt/hqnc9QKzuXJ5njFJ+M+jm5gEb2Gt/iGRbPOWT4p/XwEqzvQaPcifENrRkvaOzwJl1PA75ItOaf9CMoi4a8yJwDiBZT5z0nHjTqkByGUPTD5RXM9uRDnJYNjX6Y/C3vQUnvwOIM6JTw1ZNaBEsW9+RBp857sS0U98zi/B/QaeTwj58hisoChfvP8EcEHbb8a5gpZK8oN8Or1vPPFXTo859GwSJtFjb7AQCtxTLgGFo88IOgjyI2LMgyPPTscdck2R1BC+PzFdoFio1ZUGA0p7nJV34I/QG86IYFvqrO2UKTAf/j27iO16Aycw1djCMNNcU1VlRmzaFNm18NY1yhSVVmAn2ej/cWLeU+6Kq5qUY7HuVfvohPNcEaOkU8IbdxVs+ikoYNBUoV2YlxRkYlw0doNuFpm9W5YUzFvgfd1jxn1vd09qId6Jc6PMsfR/oyMEUJBrzA6iYtfPFIBXuUYGvxInOTZPgsrEsTAva8ysHoh/jAqU0Ajyav5e6buwhv8rr8BoA2CwkOnUH7YTGrTwDpkBUZoBxbh9VooyR9i5V4QFkeWIkFtBvs/kRQSQ5saJczfxtn5wHL9wjtuiTNzsMqReyybFYK7SZ8Yw43HYslc6HfmIrqMhXq87zaDwMRL6h5Lwq4PKvunRBgsDLOCCVYsTIblOInq9JEKRJWZYVSeC6r4Xgox5zVWKIsXVYiQVkyhxWwQnx2b/OJLkrU5jH30oJeTFg2s4UyeUciuJc+qKJWtEPZ5g5L5I5QuqzLz+5gHvSfucuSBCEq4S0OvOzm2+TOitslpMHMULqw9f7BNgJqNkH5+mwc8m4+dKif+YhvUN89U/vHAlUtcQURqhA6LMkYx91LCvweqtHzWYo5qrLjB3VfDapslS7IUB1vxnd38FzvmHDIV3fyT7jAgRJVjQgVC3Pq1Efl0ul9zqI/WDjUZObhJqKpRR38ELcS799DqMUOdRXR2OWLWuxQV/SYTPIGVVkJaiEdW/cwAp/U2td6HeiinX8XM6BTItXqt55RA17ToZqgj9vzEpesyQkBBWnik6zLCYHCtkaDypY93FaYzKjObOKW0rg5M/kNboxbCeO58RTwW6xBD1Wyl8vJwGgPBtPNweH3uTYq1jJMamO2U1Rv26Umywy3Yc+oQdfG1WoYQtfGbdlLi8rM6Qq3l7UbVOKPI9SDt54GLMiZ2CPUSK8/LZCHxmCdonY8ezwLeJGzaca1qvtP0lUyeHJOVnzXWGzre/N/jTJ70TSWT0O/8ZvvunlnYjT7cc1eTiaEEEIIIYQQQgghhBBCCCGEEEIITX4BpH1Py9uEEC8AAAAASUVORK5CYII=\",\"sizes\":\"192x192\",\"type\":\"image/png\"}],\"start_url\":\"/\",\"display\":\"standalone\",\"orientation\":\"portrait\",\"background_color\":\"white\",\"theme_color\":\"white\"}\n"
  },
  {
    "path": "test/app/manifests/invalid-theme-color.json",
    "content": "{\n  \"start_url\": \"https://www.terra.com.br/?utm_source=homescreen\",\n  \"description\": \"Manifest with an invalid theme_color\",\n  \"theme_color\": \"not_a_real_color\"\n}\n"
  },
  {
    "path": "test/app/manifests/no-icon-array.json",
    "content": "{\n  \"name\": \"Test\",\n  \"description\": \"Manifest with icons that are not in array\",\n  \"icons\": {\n    \"16\": \"img/icons/icon16.png\",\n    \"32\": \"img/icons/icon32.png\",\n    \"60\": \"img/icons/icon60.png\",\n    \"64\": \"img/icons/icon64.png\",\n    \"90\": \"img/icons/icon90.png\",\n    \"128\": \"img/icons/icon128.png\"\n  }\n}\n"
  },
  {
    "path": "test/app/models/pwa.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n'use strict';\n\nconst assert = require('assert');\nconst Manifest = require('../../../models/manifest');\nconst Pwa = require('../../../models/pwa');\n\nconst MANIFEST_URL = 'http://www.example.com/';\n\n/* eslint-disable camelcase */\ndescribe('models/pwa.js', () => {\n  it('Create a PWA with no Manifest', () => {\n    const pwa = new Pwa();\n    assert.equal(pwa.manifestUrl, undefined);\n    assert.equal(pwa.name, '', 'returns empty name');\n    assert.equal(pwa.description, '', 'returns empty description');\n    assert.equal(pwa.startUrl, '', 'returns empty startUrl');\n    assert.equal(pwa.absoluteStartUrl, '', 'returns empty absoluteStartUrl');\n    assert.equal(pwa.backgroundColor, '#ffffff', 'backgroundColor is #ffffff');\n    assert.equal(pwa.manifest, null);\n    assert.ok(pwa.created, 'created field exists');\n    assert.ok(pwa.updated, 'updated field exists');\n    assert.ok(pwa.created instanceof Date, 'created is a Date');\n    assert.ok(pwa.updated instanceof Date, 'updated is a Date');\n    assert.equal(pwa.visible, true);\n  });\n\n  it('Create a PWA with Empty Manifest', () => {\n    const manifest = new Manifest(MANIFEST_URL, {});\n    const pwa = new Pwa(MANIFEST_URL, manifest);\n    assert.equal(pwa.manifestUrl, MANIFEST_URL);\n    assert.equal(pwa.manifest.raw, '{}');\n    assert.equal(pwa.name, '', 'returns empty name');\n    assert.equal(pwa.description, '', 'returns empty description');\n    assert.equal(pwa.startUrl, '', 'returns empty startUrl');\n    assert.equal(pwa.absoluteStartUrl, 'http://www.example.com/', 'returns absoluteStartUrl');\n    assert.equal(pwa.backgroundColor, '#ffffff', 'backgroundColor is #ffffff');\n    assert.equal(pwa.manifestAsString, '{}', 'manifestAsString is {}');\n  });\n\n  it('Create a PWA with a Full Manifest', () => {\n    const manifestJson = {\n      name: 'Example PWA',\n      description: 'Example PWA',\n      icons: [{\n        src: '/icon/web_hi_res_512.png',\n        sizes: '512x512',\n        type: 'image/png'\n      }],\n      start_url: '/index.jsp',\n      display: 'standalone',\n      background_color: '#673AB7',\n      theme_color: '#512DA8'\n    };\n    const manifest = new Manifest(MANIFEST_URL, manifestJson);\n    const pwa = new Pwa(MANIFEST_URL, manifest);\n    assert.equal(pwa.manifestUrl, MANIFEST_URL);\n    assert.equal(pwa.manifest.raw, manifest.raw);\n    assert.equal(pwa.name, 'Example PWA', 'returns correct name');\n    assert.equal(pwa.description, 'Example PWA', 'correct description');\n    assert.equal(pwa.startUrl, '/index.jsp', 'returns correct startUrl');\n    assert.equal(pwa.absoluteStartUrl, 'http://www.example.com/index.jsp', 'returns empty absoluteStartUrl');\n    assert.equal(pwa.backgroundColor, '#673AB7', 'backgroundColor is #ffffff');\n    assert.ok(pwa.manifestAsString, 'manifestAsString is a non-empty string');\n  });\n\n  it('Return metaDescription if !description && !manifestDescription', () => {\n    const pwa = new Pwa();\n    assert.equal(pwa.description, '');\n    pwa.metaDescription = 'metaDescription';\n    assert.equal(pwa.description, 'metaDescription');\n  });\n\n  it('Return manifestDescription if !description', () => {\n    const pwa = new Pwa();\n    assert.equal(pwa.description, '');\n    pwa.metaDescription = 'metaDescription';\n    assert.equal(pwa.description, 'metaDescription');\n    pwa.manifest = new Manifest('http://www.example.com', {description: 'manifestDescription'});\n    assert.equal(pwa.description, 'manifestDescription');\n  });\n\n  describe('absoluteStartUrl', () => {\n    it('works with relative paths', () => {\n      const pwa = this.newPwa('https://www.example.com', {\n        start_url: '/'\n      });\n      assert.equal(pwa.absoluteStartUrl, 'https://www.example.com/');\n    });\n\n    it('works with absolute paths', () => {\n      const pwa = this.newPwa('https://www.example.com', {\n        start_url: 'https://www.example.com'\n      });\n      assert.equal(pwa.absoluteStartUrl, 'https://www.example.com/');\n    });\n\n    it('removes utm_* query parameters', () => {\n      const pwa = this.newPwa('https://www.example.com', {\n        start_url: 'https://www.example.com?utm_source=test&homescreen=1'\n      });\n      assert.equal(pwa.absoluteStartUrl, 'https://www.example.com/?homescreen=1');\n    });\n  });\n\n  describe('displayName', () => {\n    it('is name', () => {\n      const pwa = this.newPwa('www.manifesturl.com', {\n        name: 'Example PWA',\n        short_name: 'PWA'\n      });\n      assert.equal(pwa.displayName, 'Example PWA');\n    });\n    it('is short name', () => {\n      const pwa = this.newPwa('www.manifesturl.com', {\n        short_name: 'PWA'\n      });\n      assert.equal(pwa.displayName, 'PWA');\n    });\n    it('is url', () => {\n      const pwa = this.newPwa('www.manifesturl.com', {\n      });\n      assert.equal(pwa.displayName, 'www.manifesturl.com');\n    });\n    it('is url without file name', () => {\n      const pwa = this.newPwa('www.manifesturl.com/manifest.json', {\n      });\n      assert.equal(pwa.displayName, 'www.manifesturl.com');\n    });\n    it('is url without scheme', () => {\n      const pwa = this.newPwa('https://www.manifesturl.com/manifest.json', {\n      });\n      assert.equal(pwa.displayName, 'www.manifesturl.com');\n    });\n  });\n});\n\n/**\n * Creates a PWA object from a Manifest URL and Json Data for testing.\n *\n * @param {string} manifestUrl the URL for the Manifest\n * @param {Json} manifestData the Json object with the Manifest data\n * @return {Pwa}\n */\nexports.newPwa = function(manifestUrl, manifestData) {\n  const manifest = new Manifest(manifestUrl, manifestData);\n  return new Pwa(manifestUrl, manifest);\n};\n"
  },
  {
    "path": "test/app/views/helpers/index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it */\n'use strict';\n\nlet assert = require('assert');\nlet helpers = require('../../../../views/helpers');\n\ndescribe('views.helpers', () => {\n  describe('#contrastColor', () => {\n    it('should return \"white\" when the value is not present', () => {\n      assert.equal('#000000', helpers.contrastColor(null));\n      assert.equal('#000000', helpers.contrastColor(''));\n      assert.equal('#000000', helpers.contrastColor('transparent'));\n    });\n\n    it('it should understand HTML colors by name', () => {\n      assert.equal('#ffffff', helpers.contrastColor('black'));\n      assert.equal('#000000', helpers.contrastColor('white'));\n    });\n  });\n\n  describe('#firstLetter', () => {\n    it('should return an empty string when value is not present', () => {\n      assert.equal('', helpers.firstLetter(null));\n    });\n\n    it('should return \"G\" for \"Gulliver\"', () => {\n      assert.equal('G', helpers.firstLetter('Gulliver'));\n    });\n\n    it('should return \"G\" for \"gulliver\"', () => {\n      assert.equal('G', helpers.firstLetter('gulliver'));\n    });\n  });\n\n  it('#moment', () => {\n    assert.ok(helpers.moment());\n    assert.ok(helpers.moment(null));\n    assert.ok(helpers.moment(new Date()), 'Returns a value for a Date');\n  });\n});\n"
  },
  {
    "path": "test/client/js/event-target.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* global describe it beforeEach afterEach */\n'use strict';\nimport EventTarget from '../../../public/js/event-target';\nconst assert = require('assert');\nconst simpleMock = require('simple-mock');\n\ndescribe('js.event-target', () => {\n  let eventTarget;\n  const callbackA = simpleMock.spy(() => {});\n\n  beforeEach(() => {\n    eventTarget = new EventTarget();\n  });\n\n  afterEach(() => {\n    simpleMock.restore();\n    callbackA.reset();\n  });\n\n  it('Fires the correct callback', () => {\n    eventTarget.addEventListener('event-a', callbackA);\n    eventTarget.dispatchEvent({type: 'event-a'});\n    assert.equal(callbackA.callCount, 1);\n  });\n\n  it('Does not invoke a removed callback', () => {\n    eventTarget.addEventListener('event-a', callbackA);\n    eventTarget.removeEventListener('event-a', callbackA);\n    eventTarget.dispatchEvent({type: 'event-a'});\n    assert.equal(callbackA.callCount, 0);\n  });\n});\n"
  },
  {
    "path": "third_party/Color.js",
    "content": "/*\n * Copyright (C) 2009 Apple Inc.  All rights reserved.\n * Copyright (C) 2009 Joseph Pecoraro\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n *\n * 1.  Redistributions of source code must retain the above copyright\n *     notice, this list of conditions and the following disclaimer.\n * 2.  Redistributions in binary form must reproduce the above copyright\n *     notice, this list of conditions and the following disclaimer in the\n *     documentation and/or other materials provided with the distribution.\n * 3.  Neither the name of Apple Computer, Inc. (\"Apple\") nor the names of\n *     its contributors may be used to endorse or promote products derived\n *     from this software without specific prior written permission.\n *\n * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS \"AS IS\" AND ANY\n * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\n * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY\n * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND\n * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\n * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n */\n\n/**\n * @unrestricted\n */\nCommon.Color = class {\n  /**\n   * @param {!Array.<number>} rgba\n   * @param {!Common.Color.Format} format\n   * @param {string=} originalText\n   */\n  constructor(rgba, format, originalText) {\n    this._rgba = rgba;\n    this._originalText = originalText || null;\n    this._originalTextIsValid = !!this._originalText;\n    this._format = format;\n    if (typeof this._rgba[3] === 'undefined')\n      this._rgba[3] = 1;\n\n    for (var i = 0; i < 4; ++i) {\n      if (this._rgba[i] < 0) {\n        this._rgba[i] = 0;\n        this._originalTextIsValid = false;\n      }\n      if (this._rgba[i] > 1) {\n        this._rgba[i] = 1;\n        this._originalTextIsValid = false;\n      }\n    }\n  }\n\n  /**\n   * @param {string} text\n   * @return {?Common.Color}\n   */\n  static parse(text) {\n    // Simple - #hex, rgb(), nickname, hsl()\n    var value = text.toLowerCase().replace(/\\s+/g, '');\n    var simple =\n        /^(?:#([0-9a-f]{3}|[0-9a-f]{6})|rgb\\(((?:-?\\d+%?,){2}-?\\d+%?)\\)|(\\w+)|hsl\\((-?\\d+\\.?\\d*(?:,-?\\d+\\.?\\d*%){2})\\))$/i;\n    var match = value.match(simple);\n    if (match) {\n      if (match[1]) {  // hex\n        var hex = match[1].toLowerCase();\n        var format;\n        if (hex.length === 3) {\n          format = Common.Color.Format.ShortHEX;\n          hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);\n        } else {\n          format = Common.Color.Format.HEX;\n        }\n        var r = parseInt(hex.substring(0, 2), 16);\n        var g = parseInt(hex.substring(2, 4), 16);\n        var b = parseInt(hex.substring(4, 6), 16);\n        return new Common.Color([r / 255, g / 255, b / 255, 1], format, text);\n      }\n\n      if (match[2]) {  // rgb\n        var rgbString = match[2].split(/\\s*,\\s*/);\n        var rgba = [\n          Common.Color._parseRgbNumeric(rgbString[0]), Common.Color._parseRgbNumeric(rgbString[1]),\n          Common.Color._parseRgbNumeric(rgbString[2]), 1\n        ];\n        return new Common.Color(rgba, Common.Color.Format.RGB, text);\n      }\n\n      if (match[3]) {  // nickname\n        var nickname = match[3].toLowerCase();\n        if (nickname in Common.Color.Nicknames) {\n          var rgba = Common.Color.Nicknames[nickname];\n          var color = Common.Color.fromRGBA(rgba);\n          color._format = Common.Color.Format.Nickname;\n          color._originalText = text;\n          return color;\n        }\n        return null;\n      }\n\n      if (match[4]) {  // hsl\n        var hslString = match[4].replace(/%/g, '').split(/\\s*,\\s*/);\n        var hsla = [\n          Common.Color._parseHueNumeric(hslString[0]), Common.Color._parseSatLightNumeric(hslString[1]),\n          Common.Color._parseSatLightNumeric(hslString[2]), 1\n        ];\n        var rgba = [];\n        Common.Color.hsl2rgb(hsla, rgba);\n        return new Common.Color(rgba, Common.Color.Format.HSL, text);\n      }\n\n      return null;\n    }\n\n    // Advanced - rgba(), hsla()\n    var advanced =\n        /^(?:rgba\\(((?:-?\\d+%?,){3}-?(?:\\d+|\\d*\\.\\d+))\\)|hsla\\((-?(?:\\d+|\\d*\\.\\d+)(?:,-?(?:\\d+|\\d*\\.\\d+)*%){2},-?(?:\\d+|\\d*\\.\\d+))\\))$/;\n    match = value.match(advanced);\n    if (match) {\n      if (match[1]) {  // rgba\n        var rgbaString = match[1].split(/\\s*,\\s*/);\n        var rgba = [\n          Common.Color._parseRgbNumeric(rgbaString[0]), Common.Color._parseRgbNumeric(rgbaString[1]),\n          Common.Color._parseRgbNumeric(rgbaString[2]), Common.Color._parseAlphaNumeric(rgbaString[3])\n        ];\n        return new Common.Color(rgba, Common.Color.Format.RGBA, text);\n      }\n\n      if (match[2]) {  // hsla\n        var hslaString = match[2].replace(/%/g, '').split(/\\s*,\\s*/);\n        var hsla = [\n          Common.Color._parseHueNumeric(hslaString[0]), Common.Color._parseSatLightNumeric(hslaString[1]),\n          Common.Color._parseSatLightNumeric(hslaString[2]), Common.Color._parseAlphaNumeric(hslaString[3])\n        ];\n        var rgba = [];\n        Common.Color.hsl2rgb(hsla, rgba);\n        return new Common.Color(rgba, Common.Color.Format.HSLA, text);\n      }\n    }\n\n    return null;\n  }\n\n  /**\n   * @param {!Array.<number>} rgba\n   * @return {!Common.Color}\n   */\n  static fromRGBA(rgba) {\n    return new Common.Color([rgba[0] / 255, rgba[1] / 255, rgba[2] / 255, rgba[3]], Common.Color.Format.RGBA);\n  }\n\n  /**\n   * @param {!Array.<number>} hsva\n   * @return {!Common.Color}\n   */\n  static fromHSVA(hsva) {\n    var rgba = [];\n    Common.Color.hsva2rgba(hsva, rgba);\n    return new Common.Color(rgba, Common.Color.Format.HSLA);\n  }\n\n  /**\n   * @param {string} value\n   * return {number}\n   */\n  static _parseRgbNumeric(value) {\n    var parsed = parseInt(value, 10);\n    if (value.indexOf('%') !== -1)\n      parsed /= 100;\n    else\n      parsed /= 255;\n    return parsed;\n  }\n\n  /**\n   * @param {string} value\n   * return {number}\n   */\n  static _parseHueNumeric(value) {\n    return isNaN(value) ? 0 : (parseFloat(value) / 360) % 1;\n  }\n\n  /**\n   * @param {string} value\n   * return {number}\n   */\n  static _parseSatLightNumeric(value) {\n    return Math.min(1, parseFloat(value) / 100);\n  }\n\n  /**\n   * @param {string} value\n   * return {number}\n   */\n  static _parseAlphaNumeric(value) {\n    return isNaN(value) ? 0 : parseFloat(value);\n  }\n\n  /**\n   * @param {!Array.<number>} hsva\n   * @param {!Array.<number>} out_hsla\n   */\n  static _hsva2hsla(hsva, out_hsla) {\n    var h = hsva[0];\n    var s = hsva[1];\n    var v = hsva[2];\n\n    var t = (2 - s) * v;\n    if (v === 0 || s === 0)\n      s = 0;\n    else\n      s *= v / (t < 1 ? t : 2 - t);\n\n    out_hsla[0] = h;\n    out_hsla[1] = s;\n    out_hsla[2] = t / 2;\n    out_hsla[3] = hsva[3];\n  }\n\n  /**\n   * @param {!Array.<number>} hsl\n   * @param {!Array.<number>} out_rgb\n   */\n  static hsl2rgb(hsl, out_rgb) {\n    var h = hsl[0];\n    var s = hsl[1];\n    var l = hsl[2];\n\n    function hue2rgb(p, q, h) {\n      if (h < 0)\n        h += 1;\n      else if (h > 1)\n        h -= 1;\n\n      if ((h * 6) < 1)\n        return p + (q - p) * h * 6;\n      else if ((h * 2) < 1)\n        return q;\n      else if ((h * 3) < 2)\n        return p + (q - p) * ((2 / 3) - h) * 6;\n      else\n        return p;\n    }\n\n    if (s < 0)\n      s = 0;\n\n    if (l <= 0.5)\n      var q = l * (1 + s);\n    else\n      var q = l + s - (l * s);\n\n    var p = 2 * l - q;\n\n    var tr = h + (1 / 3);\n    var tg = h;\n    var tb = h - (1 / 3);\n\n    out_rgb[0] = hue2rgb(p, q, tr);\n    out_rgb[1] = hue2rgb(p, q, tg);\n    out_rgb[2] = hue2rgb(p, q, tb);\n    out_rgb[3] = hsl[3];\n  }\n\n  /**\n   * @param {!Array<number>} hsva\n   * @param {!Array<number>} out_rgba\n   */\n  static hsva2rgba(hsva, out_rgba) {\n    Common.Color._hsva2hsla(hsva, Common.Color.hsva2rgba._tmpHSLA);\n    Common.Color.hsl2rgb(Common.Color.hsva2rgba._tmpHSLA, out_rgba);\n\n    for (var i = 0; i < Common.Color.hsva2rgba._tmpHSLA.length; i++)\n      Common.Color.hsva2rgba._tmpHSLA[i] = 0;\n  }\n\n  /**\n   * Calculate the luminance of this color using the WCAG algorithm.\n   * See http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef\n   * @param {!Array<number>} rgba\n   * @return {number}\n   */\n  static luminance(rgba) {\n    var rSRGB = rgba[0];\n    var gSRGB = rgba[1];\n    var bSRGB = rgba[2];\n\n    var r = rSRGB <= 0.03928 ? rSRGB / 12.92 : Math.pow(((rSRGB + 0.055) / 1.055), 2.4);\n    var g = gSRGB <= 0.03928 ? gSRGB / 12.92 : Math.pow(((gSRGB + 0.055) / 1.055), 2.4);\n    var b = bSRGB <= 0.03928 ? bSRGB / 12.92 : Math.pow(((bSRGB + 0.055) / 1.055), 2.4);\n\n    return 0.2126 * r + 0.7152 * g + 0.0722 * b;\n  }\n\n  /**\n   * Combine the two given color according to alpha blending.\n   * @param {!Array<number>} fgRGBA\n   * @param {!Array<number>} bgRGBA\n   * @param {!Array<number>} out_blended\n   */\n  static blendColors(fgRGBA, bgRGBA, out_blended) {\n    var alpha = fgRGBA[3];\n\n    out_blended[0] = ((1 - alpha) * bgRGBA[0]) + (alpha * fgRGBA[0]);\n    out_blended[1] = ((1 - alpha) * bgRGBA[1]) + (alpha * fgRGBA[1]);\n    out_blended[2] = ((1 - alpha) * bgRGBA[2]) + (alpha * fgRGBA[2]);\n    out_blended[3] = alpha + (bgRGBA[3] * (1 - alpha));\n  }\n\n  /**\n   * Calculate the contrast ratio between a foreground and a background color.\n   * Returns the ratio to 1, for example for two two colors with a contrast ratio of 21:1, this function will return 21.\n   * See http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef\n   * @param {!Array<number>} fgRGBA\n   * @param {!Array<number>} bgRGBA\n   * @return {number}\n   */\n  static calculateContrastRatio(fgRGBA, bgRGBA) {\n    Common.Color.blendColors(fgRGBA, bgRGBA, Common.Color.calculateContrastRatio._blendedFg);\n\n    var fgLuminance = Common.Color.luminance(Common.Color.calculateContrastRatio._blendedFg);\n    var bgLuminance = Common.Color.luminance(bgRGBA);\n    var contrastRatio = (Math.max(fgLuminance, bgLuminance) + 0.05) / (Math.min(fgLuminance, bgLuminance) + 0.05);\n\n    for (var i = 0; i < Common.Color.calculateContrastRatio._blendedFg.length; i++)\n      Common.Color.calculateContrastRatio._blendedFg[i] = 0;\n\n    return contrastRatio;\n  }\n\n  /**\n   * Compute a desired luminance given a given luminance and a desired contrast\n   * ratio.\n   * @param {number} luminance The given luminance.\n   * @param {number} contrast The desired contrast ratio.\n   * @param {boolean} lighter Whether the desired luminance is lighter or darker\n   * than the given luminance. If no luminance can be found which meets this\n   * requirement, a luminance which meets the inverse requirement will be\n   * returned.\n   * @return {number} The desired luminance.\n   */\n  static desiredLuminance(luminance, contrast, lighter) {\n    function computeLuminance() {\n      if (lighter)\n        return (luminance + 0.05) * contrast - 0.05;\n      else\n        return (luminance + 0.05) / contrast - 0.05;\n    }\n    var desiredLuminance = computeLuminance();\n    if (desiredLuminance < 0 || desiredLuminance > 1) {\n      lighter = !lighter;\n      desiredLuminance = computeLuminance();\n    }\n    return desiredLuminance;\n  }\n\n  /**\n   * @param {!Common.Color} color\n   * @return {!Common.Color.Format}\n   */\n  static detectColorFormat(color) {\n    const cf = Common.Color.Format;\n    var format;\n    var formatSetting = Common.moduleSetting('colorFormat').get();\n    if (formatSetting === cf.Original)\n      format = cf.Original;\n    else if (formatSetting === cf.RGB)\n      format = (color.hasAlpha() ? cf.RGBA : cf.RGB);\n    else if (formatSetting === cf.HSL)\n      format = (color.hasAlpha() ? cf.HSLA : cf.HSL);\n    else if (!color.hasAlpha())\n      format = (color.canBeShortHex() ? cf.ShortHEX : cf.HEX);\n    else\n      format = cf.RGBA;\n\n    return format;\n  }\n\n  /**\n   * @return {!Common.Color.Format}\n   */\n  format() {\n    return this._format;\n  }\n\n  /**\n   * @return {!Array.<number>} HSLA with components within [0..1]\n   */\n  hsla() {\n    if (this._hsla)\n      return this._hsla;\n    var r = this._rgba[0];\n    var g = this._rgba[1];\n    var b = this._rgba[2];\n    var max = Math.max(r, g, b);\n    var min = Math.min(r, g, b);\n    var diff = max - min;\n    var add = max + min;\n\n    if (min === max)\n      var h = 0;\n    else if (r === max)\n      var h = ((1 / 6 * (g - b) / diff) + 1) % 1;\n    else if (g === max)\n      var h = (1 / 6 * (b - r) / diff) + 1 / 3;\n    else\n      var h = (1 / 6 * (r - g) / diff) + 2 / 3;\n\n    var l = 0.5 * add;\n\n    if (l === 0)\n      var s = 0;\n    else if (l === 1)\n      var s = 0;\n    else if (l <= 0.5)\n      var s = diff / add;\n    else\n      var s = diff / (2 - add);\n\n    this._hsla = [h, s, l, this._rgba[3]];\n    return this._hsla;\n  }\n\n  /**\n   * @return {!Array.<number>}\n   */\n  canonicalHSLA() {\n    var hsla = this.hsla();\n    return [Math.round(hsla[0] * 360), Math.round(hsla[1] * 100), Math.round(hsla[2] * 100), hsla[3]];\n  }\n\n  /**\n   * @return {!Array.<number>} HSVA with components within [0..1]\n   */\n  hsva() {\n    var hsla = this.hsla();\n    var h = hsla[0];\n    var s = hsla[1];\n    var l = hsla[2];\n\n    s *= l < 0.5 ? l : 1 - l;\n    return [h, s !== 0 ? 2 * s / (l + s) : 0, (l + s), hsla[3]];\n  }\n\n  /**\n   * @return {boolean}\n   */\n  hasAlpha() {\n    return this._rgba[3] !== 1;\n  }\n\n  /**\n   * @return {boolean}\n   */\n  canBeShortHex() {\n    if (this.hasAlpha())\n      return false;\n    for (var i = 0; i < 3; ++i) {\n      var c = Math.round(this._rgba[i] * 255);\n      if (c % 17)\n        return false;\n    }\n    return true;\n  }\n\n  /**\n   * @return {?string}\n   */\n  asString(format) {\n    if (format === this._format && this._originalTextIsValid)\n      return this._originalText;\n\n    if (!format)\n      format = this._format;\n\n    /**\n     * @param {number} value\n     * @return {number}\n     */\n    function toRgbValue(value) {\n      return Math.round(value * 255);\n    }\n\n    /**\n     * @param {number} value\n     * @return {string}\n     */\n    function toHexValue(value) {\n      var hex = Math.round(value * 255).toString(16);\n      return hex.length === 1 ? '0' + hex : hex;\n    }\n\n    /**\n     * @param {number} value\n     * @return {string}\n     */\n    function toShortHexValue(value) {\n      return (Math.round(value * 255) / 17).toString(16);\n    }\n\n    switch (format) {\n      case Common.Color.Format.Original:\n        return this._originalText;\n      case Common.Color.Format.RGB:\n        if (this.hasAlpha())\n          return null;\n        return String.sprintf(\n            'rgb(%d, %d, %d)', toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]));\n      case Common.Color.Format.RGBA:\n        return String.sprintf(\n            'rgba(%d, %d, %d, %f)', toRgbValue(this._rgba[0]), toRgbValue(this._rgba[1]), toRgbValue(this._rgba[2]),\n            this._rgba[3]);\n      case Common.Color.Format.HSL:\n        if (this.hasAlpha())\n          return null;\n        var hsl = this.hsla();\n        return String.sprintf(\n            'hsl(%d, %d%, %d%)', Math.round(hsl[0] * 360), Math.round(hsl[1] * 100), Math.round(hsl[2] * 100));\n      case Common.Color.Format.HSLA:\n        var hsla = this.hsla();\n        return String.sprintf(\n            'hsla(%d, %d%, %d%, %f)', Math.round(hsla[0] * 360), Math.round(hsla[1] * 100), Math.round(hsla[2] * 100),\n            hsla[3]);\n      case Common.Color.Format.HEX:\n        if (this.hasAlpha())\n          return null;\n        return String\n            .sprintf('#%s%s%s', toHexValue(this._rgba[0]), toHexValue(this._rgba[1]), toHexValue(this._rgba[2]))\n            .toLowerCase();\n      case Common.Color.Format.ShortHEX:\n        if (!this.canBeShortHex())\n          return null;\n        return String\n            .sprintf(\n                '#%s%s%s', toShortHexValue(this._rgba[0]), toShortHexValue(this._rgba[1]),\n                toShortHexValue(this._rgba[2]))\n            .toLowerCase();\n      case Common.Color.Format.Nickname:\n        return this.nickname();\n    }\n\n    return this._originalText;\n  }\n\n  /**\n   * @return {!Array<number>}\n   */\n  rgba() {\n    return this._rgba.slice();\n  }\n\n  /**\n   * @return {!Array.<number>}\n   */\n  canonicalRGBA() {\n    var rgba = new Array(4);\n    for (var i = 0; i < 3; ++i)\n      rgba[i] = Math.round(this._rgba[i] * 255);\n    rgba[3] = this._rgba[3];\n    return rgba;\n  }\n\n  /**\n   * @return {?string} nickname\n   */\n  nickname() {\n    if (!Common.Color._rgbaToNickname) {\n      Common.Color._rgbaToNickname = {};\n      for (var nickname in Common.Color.Nicknames) {\n        var rgba = Common.Color.Nicknames[nickname];\n        if (rgba.length !== 4)\n          rgba = rgba.concat(1);\n        Common.Color._rgbaToNickname[rgba] = nickname;\n      }\n    }\n\n    return Common.Color._rgbaToNickname[this.canonicalRGBA()] || null;\n  }\n\n  /**\n   * @return {!{r: number, g: number, b: number, a: (number|undefined)}}\n   */\n  toProtocolRGBA() {\n    var rgba = this.canonicalRGBA();\n    var result = {r: rgba[0], g: rgba[1], b: rgba[2]};\n    if (rgba[3] !== 1)\n      result.a = rgba[3];\n    return result;\n  }\n\n  /**\n   * @return {!Common.Color}\n   */\n  invert() {\n    var rgba = [];\n    rgba[0] = 1 - this._rgba[0];\n    rgba[1] = 1 - this._rgba[1];\n    rgba[2] = 1 - this._rgba[2];\n    rgba[3] = this._rgba[3];\n    return new Common.Color(rgba, Common.Color.Format.RGBA);\n  }\n\n  /**\n   * @param {number} alpha\n   * @return {!Common.Color}\n   */\n  setAlpha(alpha) {\n    var rgba = this._rgba.slice();\n    rgba[3] = alpha;\n    return new Common.Color(rgba, Common.Color.Format.RGBA);\n  }\n};\n\n/** @type {!RegExp} */\nCommon.Color.Regex = /((?:rgb|hsl)a?\\([^)]+\\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\\b[a-zA-Z]+\\b(?!-))/g;\n\n/**\n * @enum {string}\n */\nCommon.Color.Format = {\n  Original: 'original',\n  Nickname: 'nickname',\n  HEX: 'hex',\n  ShortHEX: 'shorthex',\n  RGB: 'rgb',\n  RGBA: 'rgba',\n  HSL: 'hsl',\n  HSLA: 'hsla'\n};\n\n\n/** @type {!Array<number>} */\nCommon.Color.hsva2rgba._tmpHSLA = [0, 0, 0, 0];\n\n\nCommon.Color.calculateContrastRatio._blendedFg = [0, 0, 0, 0];\n\n\nCommon.Color.Nicknames = {\n  'aliceblue': [240, 248, 255],\n  'antiquewhite': [250, 235, 215],\n  'aqua': [0, 255, 255],\n  'aquamarine': [127, 255, 212],\n  'azure': [240, 255, 255],\n  'beige': [245, 245, 220],\n  'bisque': [255, 228, 196],\n  'black': [0, 0, 0],\n  'blanchedalmond': [255, 235, 205],\n  'blue': [0, 0, 255],\n  'blueviolet': [138, 43, 226],\n  'brown': [165, 42, 42],\n  'burlywood': [222, 184, 135],\n  'cadetblue': [95, 158, 160],\n  'chartreuse': [127, 255, 0],\n  'chocolate': [210, 105, 30],\n  'coral': [255, 127, 80],\n  'cornflowerblue': [100, 149, 237],\n  'cornsilk': [255, 248, 220],\n  'crimson': [237, 20, 61],\n  'cyan': [0, 255, 255],\n  'darkblue': [0, 0, 139],\n  'darkcyan': [0, 139, 139],\n  'darkgoldenrod': [184, 134, 11],\n  'darkgray': [169, 169, 169],\n  'darkgrey': [169, 169, 169],\n  'darkgreen': [0, 100, 0],\n  'darkkhaki': [189, 183, 107],\n  'darkmagenta': [139, 0, 139],\n  'darkolivegreen': [85, 107, 47],\n  'darkorange': [255, 140, 0],\n  'darkorchid': [153, 50, 204],\n  'darkred': [139, 0, 0],\n  'darksalmon': [233, 150, 122],\n  'darkseagreen': [143, 188, 143],\n  'darkslateblue': [72, 61, 139],\n  'darkslategray': [47, 79, 79],\n  'darkslategrey': [47, 79, 79],\n  'darkturquoise': [0, 206, 209],\n  'darkviolet': [148, 0, 211],\n  'deeppink': [255, 20, 147],\n  'deepskyblue': [0, 191, 255],\n  'dimgray': [105, 105, 105],\n  'dimgrey': [105, 105, 105],\n  'dodgerblue': [30, 144, 255],\n  'firebrick': [178, 34, 34],\n  'floralwhite': [255, 250, 240],\n  'forestgreen': [34, 139, 34],\n  'fuchsia': [255, 0, 255],\n  'gainsboro': [220, 220, 220],\n  'ghostwhite': [248, 248, 255],\n  'gold': [255, 215, 0],\n  'goldenrod': [218, 165, 32],\n  'gray': [128, 128, 128],\n  'grey': [128, 128, 128],\n  'green': [0, 128, 0],\n  'greenyellow': [173, 255, 47],\n  'honeydew': [240, 255, 240],\n  'hotpink': [255, 105, 180],\n  'indianred': [205, 92, 92],\n  'indigo': [75, 0, 130],\n  'ivory': [255, 255, 240],\n  'khaki': [240, 230, 140],\n  'lavender': [230, 230, 250],\n  'lavenderblush': [255, 240, 245],\n  'lawngreen': [124, 252, 0],\n  'lemonchiffon': [255, 250, 205],\n  'lightblue': [173, 216, 230],\n  'lightcoral': [240, 128, 128],\n  'lightcyan': [224, 255, 255],\n  'lightgoldenrodyellow': [250, 250, 210],\n  'lightgreen': [144, 238, 144],\n  'lightgray': [211, 211, 211],\n  'lightgrey': [211, 211, 211],\n  'lightpink': [255, 182, 193],\n  'lightsalmon': [255, 160, 122],\n  'lightseagreen': [32, 178, 170],\n  'lightskyblue': [135, 206, 250],\n  'lightslategray': [119, 136, 153],\n  'lightslategrey': [119, 136, 153],\n  'lightsteelblue': [176, 196, 222],\n  'lightyellow': [255, 255, 224],\n  'lime': [0, 255, 0],\n  'limegreen': [50, 205, 50],\n  'linen': [250, 240, 230],\n  'magenta': [255, 0, 255],\n  'maroon': [128, 0, 0],\n  'mediumaquamarine': [102, 205, 170],\n  'mediumblue': [0, 0, 205],\n  'mediumorchid': [186, 85, 211],\n  'mediumpurple': [147, 112, 219],\n  'mediumseagreen': [60, 179, 113],\n  'mediumslateblue': [123, 104, 238],\n  'mediumspringgreen': [0, 250, 154],\n  'mediumturquoise': [72, 209, 204],\n  'mediumvioletred': [199, 21, 133],\n  'midnightblue': [25, 25, 112],\n  'mintcream': [245, 255, 250],\n  'mistyrose': [255, 228, 225],\n  'moccasin': [255, 228, 181],\n  'navajowhite': [255, 222, 173],\n  'navy': [0, 0, 128],\n  'oldlace': [253, 245, 230],\n  'olive': [128, 128, 0],\n  'olivedrab': [107, 142, 35],\n  'orange': [255, 165, 0],\n  'orangered': [255, 69, 0],\n  'orchid': [218, 112, 214],\n  'palegoldenrod': [238, 232, 170],\n  'palegreen': [152, 251, 152],\n  'paleturquoise': [175, 238, 238],\n  'palevioletred': [219, 112, 147],\n  'papayawhip': [255, 239, 213],\n  'peachpuff': [255, 218, 185],\n  'peru': [205, 133, 63],\n  'pink': [255, 192, 203],\n  'plum': [221, 160, 221],\n  'powderblue': [176, 224, 230],\n  'purple': [128, 0, 128],\n  'rebeccapurple': [102, 51, 153],\n  'red': [255, 0, 0],\n  'rosybrown': [188, 143, 143],\n  'royalblue': [65, 105, 225],\n  'saddlebrown': [139, 69, 19],\n  'salmon': [250, 128, 114],\n  'sandybrown': [244, 164, 96],\n  'seagreen': [46, 139, 87],\n  'seashell': [255, 245, 238],\n  'sienna': [160, 82, 45],\n  'silver': [192, 192, 192],\n  'skyblue': [135, 206, 235],\n  'slateblue': [106, 90, 205],\n  'slategray': [112, 128, 144],\n  'slategrey': [112, 128, 144],\n  'snow': [255, 250, 250],\n  'springgreen': [0, 255, 127],\n  'steelblue': [70, 130, 180],\n  'tan': [210, 180, 140],\n  'teal': [0, 128, 128],\n  'thistle': [216, 191, 216],\n  'tomato': [255, 99, 71],\n  'turquoise': [64, 224, 208],\n  'violet': [238, 130, 238],\n  'wheat': [245, 222, 179],\n  'white': [255, 255, 255],\n  'whitesmoke': [245, 245, 245],\n  'yellow': [255, 255, 0],\n  'yellowgreen': [154, 205, 50],\n  'transparent': [0, 0, 0, 0],\n};\n\nCommon.Color.PageHighlight = {\n  Content: Common.Color.fromRGBA([111, 168, 220, .66]),\n  ContentLight: Common.Color.fromRGBA([111, 168, 220, .5]),\n  ContentOutline: Common.Color.fromRGBA([9, 83, 148]),\n  Padding: Common.Color.fromRGBA([147, 196, 125, .55]),\n  PaddingLight: Common.Color.fromRGBA([147, 196, 125, .4]),\n  Border: Common.Color.fromRGBA([255, 229, 153, .66]),\n  BorderLight: Common.Color.fromRGBA([255, 229, 153, .5]),\n  Margin: Common.Color.fromRGBA([246, 178, 107, .66]),\n  MarginLight: Common.Color.fromRGBA([246, 178, 107, .5]),\n  EventTarget: Common.Color.fromRGBA([255, 196, 196, .66]),\n  Shape: Common.Color.fromRGBA([96, 82, 177, 0.8]),\n  ShapeMargin: Common.Color.fromRGBA([96, 82, 127, .6])\n};\n"
  },
  {
    "path": "third_party/README.md",
    "content": "See `./install.sh` for information on where the `*.js` files in this directory\ncome from, and how to generate them.\n"
  },
  {
    "path": "third_party/install.sh",
    "content": "#!/usr/bin/env sh\n\n# Downloads and patches the two files from lighthouse and devtools that we need\n# to validate manifests.\n#\n# (These files are part of lighthouse, but depending on lighthouse would haul in\n# multiple MBs of dependencies. So, we'll just dump the files here.)\n\ncurl -sO https://raw.githubusercontent.com/GoogleChrome/lighthouse/master/lighthouse-core/lib/manifest-parser.js\ncurl -sO https://raw.githubusercontent.com/ChromeDevTools/devtools-frontend/master/front_end/common/Color.js\n\n# Generate patch via `git diff manifest-parser.js Color.js`\ngit apply - << END\ndiff --git i/third_party/manifest-parser.js w/third_party/manifest-parser.js\nindex 5ac9d72..b9e7ead 100644\n--- i/third_party/manifest-parser.js\n+++ w/third_party/manifest-parser.js\n@@ -17,7 +17,10 @@\n 'use strict';\n\n const url = require('url');\n-const validateColor = require('./web-inspector').Color.parse;\n+\n+global.Common = {}; // the global is unfortunate, but necessary\n+require('./Color.js');\n+const validateColor = global.Common.Color.parse;\n\n const ALLOWED_DISPLAY_VALUES = [\n   'fullscreen',\nEND\n"
  },
  {
    "path": "third_party/manifest-parser.js",
    "content": "/**\n * @license\n * Copyright 2016 Google Inc. All Rights Reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n'use strict';\n\nconst url = require('url');\n\nglobal.Common = {}; // the global is unfortunate, but necessary\nrequire('./Color.js');\nconst validateColor = global.Common.Color.parse;\n\nconst ALLOWED_DISPLAY_VALUES = [\n  'fullscreen',\n  'standalone',\n  'minimal-ui',\n  'browser'\n];\n/**\n * All display-mode fallbacks, including when unset, lead to default display mode 'browser'.\n * @see https://w3c.github.io/manifest/#dfn-default-display-mode\n */\nconst DEFAULT_DISPLAY_MODE = 'browser';\n\nconst ALLOWED_ORIENTATION_VALUES = [\n  'any',\n  'natural',\n  'landscape',\n  'portrait',\n  'portrait-primary',\n  'portrait-secondary',\n  'landscape-primary',\n  'landscape-secondary'\n];\n\nfunction parseString(raw, trim) {\n  let value;\n  let debugString;\n\n  if (typeof raw === 'string') {\n    value = trim ? raw.trim() : raw;\n  } else {\n    if (raw !== undefined) {\n      debugString = 'ERROR: expected a string.';\n    }\n    value = undefined;\n  }\n\n  return {\n    raw,\n    value,\n    debugString\n  };\n}\n\nfunction parseColor(raw) {\n  const color = parseString(raw);\n\n  // Finished if color missing or not a string.\n  if (color.value === undefined) {\n    return color;\n  }\n\n  // Use DevTools's color parser to check CSS3 Color parsing.\n  const validatedColor = validateColor(color.raw);\n  if (!validatedColor) {\n    color.value = undefined;\n    color.debugString = 'ERROR: color parsing failed.';\n  }\n\n  return color;\n}\n\nfunction parseName(jsonInput) {\n  return parseString(jsonInput.name, true);\n}\n\nfunction parseShortName(jsonInput) {\n  return parseString(jsonInput.short_name, true);\n}\n\n/**\n * Returns whether the urls are of the same origin. See https://html.spec.whatwg.org/#same-origin\n * @param {string} url1\n * @param {string} url2\n * @return {boolean}\n */\nfunction checkSameOrigin(url1, url2) {\n  const parsed1 = url.parse(url1);\n  const parsed2 = url.parse(url2);\n\n  return parsed1.protocol === parsed2.protocol &&\n      parsed1.hostname === parsed2.hostname &&\n      parsed1.port === parsed2.port;\n}\n\n/**\n * https://w3c.github.io/manifest/#start_url-member\n */\nfunction parseStartUrl(jsonInput, manifestUrl, documentUrl) {\n  const raw = jsonInput.start_url;\n\n  // 8.10(3) - discard the empty string and non-strings.\n  if (raw === '') {\n    return {\n      raw,\n      value: documentUrl,\n      debugString: 'ERROR: start_url string empty'\n    };\n  }\n  const parsedAsString = parseString(raw);\n  if (!parsedAsString.value) {\n    parsedAsString.value = documentUrl;\n    return parsedAsString;\n  }\n\n  // 8.10(4) - construct URL with raw as input and manifestUrl as the base.\n  let startUrl;\n  try {\n    // TODO(bckenny): need better URL constructor to do this properly. See\n    // https://github.com/GoogleChrome/lighthouse/issues/602\n    startUrl = url.resolve(manifestUrl, raw);\n  } catch (e) {\n    // 8.10(5) - discard invalid URLs.\n    return {\n      raw,\n      value: documentUrl,\n      debugString: 'ERROR: invalid start_url relative to ${manifestUrl}'\n    };\n  }\n\n  // 8.10(6) - discard start_urls that are not same origin as documentUrl.\n  if (!checkSameOrigin(startUrl, documentUrl)) {\n    return {\n      raw,\n      value: documentUrl,\n      debugString: 'ERROR: start_url must be same-origin as document'\n    };\n  }\n\n  return {\n    raw,\n    value: startUrl\n  };\n}\n\nfunction parseDisplay(jsonInput) {\n  const display = parseString(jsonInput.display, true);\n\n  if (!display.value) {\n    display.value = DEFAULT_DISPLAY_MODE;\n    return display;\n  }\n\n  display.value = display.value.toLowerCase();\n  if (ALLOWED_DISPLAY_VALUES.indexOf(display.value) === -1) {\n    display.debugString = 'ERROR: \\'display\\' has invalid value ' + display.value +\n        ` will fall back to ${DEFAULT_DISPLAY_MODE}.`;\n    display.value = DEFAULT_DISPLAY_MODE;\n  }\n\n  return display;\n}\n\nfunction parseOrientation(jsonInput) {\n  const orientation = parseString(jsonInput.orientation, true);\n\n  if (orientation.value &&\n      ALLOWED_ORIENTATION_VALUES.indexOf(orientation.value.toLowerCase()) === -1) {\n    orientation.value = undefined;\n    orientation.debugString = 'ERROR: \\'orientation\\' has an invalid value, will be ignored.';\n  }\n\n  return orientation;\n}\n\nfunction parseIcon(raw, manifestUrl) {\n  // 9.4(3)\n  const src = parseString(raw.src, true);\n  // 9.4(4) - discard if trimmed value is the empty string.\n  if (src.value === '') {\n    src.value = undefined;\n  }\n  if (src.value) {\n    // TODO(bckenny): need better URL constructor to do this properly. See\n    // https://github.com/GoogleChrome/lighthouse/issues/602\n    // 9.4(4) - construct URL with manifest URL as the base\n    src.value = url.resolve(manifestUrl, src.value);\n  }\n\n  const type = parseString(raw.type, true);\n\n  const density = {\n    raw: raw.density,\n    value: 1,\n    debugString: undefined\n  };\n  if (density.raw !== undefined) {\n    density.value = parseFloat(density.raw);\n    if (isNaN(density.value) || !isFinite(density.value) || density.value <= 0) {\n      density.value = 1;\n      density.debugString = 'ERROR: icon density cannot be NaN, +∞, or less than or equal to +0.';\n    }\n  }\n\n  const sizes = parseString(raw.sizes);\n  if (sizes.value !== undefined) {\n    const set = new Set();\n    sizes.value.trim().split(/\\s+/).forEach(size => set.add(size.toLowerCase()));\n    sizes.value = set.size > 0 ? Array.from(set) : undefined;\n  }\n\n  return {\n    raw,\n    value: {\n      src,\n      type,\n      density,\n      sizes\n    },\n    debugString: undefined\n  };\n}\n\nfunction parseIcons(jsonInput, manifestUrl) {\n  const raw = jsonInput.icons;\n\n  if (raw === undefined) {\n    return {\n      raw,\n      value: [],\n      debugString: undefined\n    };\n  }\n\n  if (!Array.isArray(raw)) {\n    return {\n      raw,\n      value: [],\n      debugString: 'ERROR: \\'icons\\' expected to be an array but is not.'\n    };\n  }\n\n  // TODO(bckenny): spec says to skip icons missing `src`, so debug messages on\n  // individual icons are lost. Warn instead?\n  const value = raw\n    // 9.6(3)(1)\n    .filter(icon => icon.src !== undefined)\n    // 9.6(3)(2)(1)\n    .map(icon => parseIcon(icon, manifestUrl))\n    // 9.6(3)(2)(2)\n    .filter(parsedIcon => parsedIcon.value.src.value !== undefined);\n\n  return {\n    raw,\n    value,\n    debugString: undefined\n  };\n}\n\nfunction parseApplication(raw) {\n  const platform = parseString(raw.platform, true);\n  const id = parseString(raw.id, true);\n\n  // 10.2.(2) and 10.2.(3)\n  const appUrl = parseString(raw.url, true);\n  if (appUrl.value) {\n    try {\n      // TODO(bckenny): need better URL constructor to do this properly. See\n      // https://github.com/GoogleChrome/lighthouse/issues/602\n      // 10.2.(4) - attempt to construct URL.\n      appUrl.value = url.parse(appUrl.value).href;\n    } catch (e) {\n      appUrl.value = undefined;\n      appUrl.debugString = 'ERROR: invalid application URL ${raw.url}';\n    }\n  }\n\n  return {\n    raw,\n    value: {\n      platform,\n      id,\n      url: appUrl\n    },\n    debugString: undefined\n  };\n}\n\nfunction parseRelatedApplications(jsonInput) {\n  const raw = jsonInput.related_applications;\n\n  if (raw === undefined) {\n    return {\n      raw,\n      value: undefined,\n      debugString: undefined\n    };\n  }\n\n  if (!Array.isArray(raw)) {\n    return {\n      raw,\n      value: undefined,\n      debugString: 'ERROR: \\'related_applications\\' expected to be an array but is not.'\n    };\n  }\n\n  // TODO(bckenny): spec says to skip apps missing `platform`, so debug messages\n  // on individual apps are lost. Warn instead?\n  const value = raw\n    .filter(application => !!application.platform)\n    .map(parseApplication)\n    .filter(parsedApp => !!parsedApp.value.id.value || !!parsedApp.value.url.value);\n\n  return {\n    raw,\n    value,\n    debugString: undefined\n  };\n}\n\nfunction parsePreferRelatedApplications(jsonInput) {\n  const raw = jsonInput.prefer_related_applications;\n  let value;\n  let debugString;\n\n  if (typeof raw === 'boolean') {\n    value = raw;\n  } else {\n    if (raw !== undefined) {\n      debugString = 'ERROR: \\'prefer_related_applications\\' expected to be a boolean.';\n    }\n    value = undefined;\n  }\n\n  return {\n    raw,\n    value,\n    debugString\n  };\n}\n\nfunction parseThemeColor(jsonInput) {\n  return parseColor(jsonInput.theme_color);\n}\n\nfunction parseBackgroundColor(jsonInput) {\n  return parseColor(jsonInput.background_color);\n}\n\n/**\n * Parse a manifest from the given inputs.\n * @param {string} string Manifest JSON string.\n * @param {string} manifestUrl URL of manifest file.\n * @param {string} documentUrl URL of document containing manifest link element.\n * @return {!ManifestNode<(!Manifest|undefined)>}\n */\nfunction parse(string, manifestUrl, documentUrl) {\n  if (manifestUrl === undefined || documentUrl === undefined) {\n    throw new Error('Manifest and document URLs required for manifest parsing.');\n  }\n\n  let jsonInput;\n\n  try {\n    jsonInput = JSON.parse(string);\n  } catch (e) {\n    return {\n      raw: string,\n      value: undefined,\n      debugString: 'ERROR: file isn\\'t valid JSON: ' + e\n    };\n  }\n\n  /* eslint-disable camelcase */\n  const manifest = {\n    name: parseName(jsonInput),\n    short_name: parseShortName(jsonInput),\n    start_url: parseStartUrl(jsonInput, manifestUrl, documentUrl),\n    display: parseDisplay(jsonInput),\n    orientation: parseOrientation(jsonInput),\n    icons: parseIcons(jsonInput, manifestUrl),\n    related_applications: parseRelatedApplications(jsonInput),\n    prefer_related_applications: parsePreferRelatedApplications(jsonInput),\n    theme_color: parseThemeColor(jsonInput),\n    background_color: parseBackgroundColor(jsonInput)\n  };\n  /* eslint-enable camelcase */\n\n  return {\n    raw: string,\n    value: manifest,\n    debugString: undefined\n  };\n}\n\nmodule.exports = parse;\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  // https://www.typescriptlang.org/docs/handbook/compiler-options.html\n  \"compilerOptions\": {\n    \"target\": \"es5\"\n  }\n}\n"
  },
  {
    "path": "views/404.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n\n{{#unless contentOnly}}\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    {{> head}}\n  </head>\n  <body>\n    {{> header}}\n    <div class=\"page-holder\">\n      <main class=\"page\">\n{{/unless}}\n    <div class=\"section-title\">Page Not Found</div>\n{{#unless contentOnly}}\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n{{/unless}}\n"
  },
  {
    "path": "views/app/offline.hbs",
    "content": "{{!--\nCopyright 2015-2016, Google, Inc.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n  http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n--}}\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <title>Gulliver - PWA Directory - Offline</title>\n    {{> head}}\n  </head>\n  <body>\n    {{> header}}    \n    <div class=\"section-title\">Offline</div>\n    <div class=\"page-holder\">\n      <main class=\"page\">\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n"
  },
  {
    "path": "views/app/shell.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n \n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    {{> head}}\n  </head>\n  <body data-empty-shell>\n    {{> header}}\n    <div class=\"page-holder\">\n      <main class=\"page\">\n        <!-- Content loaded from the shell goes here -->\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n"
  },
  {
    "path": "views/helpers/index.js",
    "content": "/**\n * Copyright 2015-2016, Google, Inc.\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *  http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n'use strict';\nconst DEFAULT_LIGHT = '#ffffff';\nconst DEFAULT_DARK = '#000000';\n\nconst escapeHtml = require('escape-html');\nconst moment = require('moment');\nconst {bestContrastRatio} = require('../../lib/color');\nconst assetHashing = require('../../lib/asset-hashing').asset;\n\nfunction contrastColor(hexcolor) {\n  if (!hexcolor) {\n    return DEFAULT_DARK;\n  }\n\n  try {\n    return bestContrastRatio(DEFAULT_DARK, DEFAULT_LIGHT, hexcolor);\n  } catch (e) {\n    return DEFAULT_DARK;\n  }\n}\n\nexports.contrastColor = contrastColor;\n\nexports.firstLetter = function(text) {\n  return (text ? text[0] : '').toUpperCase();\n};\n\nexports.moment = function(date) {\n  return moment(date).fromNow();\n};\n\nexports.themeFragment = function(pwa) {\n  return 't=' + encodeURIComponent(pwa.displayName) +\n    '&bg=' + encodeURIComponent(pwa.backgroundColor) +\n    '&c=' + encodeURIComponent(contrastColor(pwa.backgroundColor));\n};\n\n/*\n  Light-weight helper function to generate\n  highlighted JSON strings\n*/\nfunction syntaxHighlight(json) {\n  json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n  /* eslint-disable */\n  return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g,\n  /* eslint-enable */\n    match => {\n      let cls = 'number';\n      if (/^\"/.test(match)) {\n        if (/:$/.test(match)) {\n          cls = 'key';\n        } else {\n          cls = 'string';\n        }\n      } else if (/true|false/.test(match)) {\n        cls = 'boolean';\n      } else if (/null/.test(match)) {\n        cls = 'null';\n      }\n      return '<span class=\"' + cls + '\">' + match + '</span>';\n    });\n}\n\nexports.prettyJson = function(object) {\n  return JSON.stringify(object, null, 4);\n};\n\nexports.highlightedJson = function(object) {\n  return syntaxHighlight(JSON.stringify(object, null, 2));\n};\n\nexports.getReportCategoryTableRow = function(reportCategory) {\n  return '<tr>' +\n    '<th scope=\"col\">' + escapeHtml(reportCategory.name) + '</th>' +\n    '<th scope=\"col\">' + Math.round(reportCategory.score) + '</th>' +\n    '</tr>';\n};\n\nexports.getAuditTableRow = function(audit) {\n  return '<tr>' +\n    '<td>' + escapeHtml(audit.result.description) + '</td>' +\n    '<td>' + audit.result.score + '</td>' +\n    '</tr>';\n};\n\nexports.truncate = function(str, len) {\n  if (str.length > len && str.length > 0) {\n    let newStr = str + ' ';\n    newStr = str.substr(0, len);\n    newStr = str.substr(0, newStr.lastIndexOf(' '));\n    newStr = (newStr.length > 0) ? newStr : str.substr(0, len);\n    return newStr + '...';\n  }\n  return str;\n};\n\nexports.equals = function(p1, p2) {\n  return p1 === p2;\n};\n\nexports.registerHelpers = function(hbs) {\n  hbs.registerHelper('firstLetter', exports.firstLetter);\n  hbs.registerHelper('contrastColor', exports.contrastColor);\n  hbs.registerHelper('themeFragment', exports.themeFragment);\n  hbs.registerHelper('moment', exports.moment);\n  hbs.registerHelper('prettyJson', exports.prettyJson);\n  hbs.registerHelper('highlightedJson', exports.highlightedJson);\n  hbs.registerHelper('getReportCategoryTableRow', exports.getReportCategoryTableRow);\n  hbs.registerHelper('getAuditTableRow', exports.getAuditTableRow);\n  hbs.registerHelper('asset', assetPath => assetHashing.encode(assetPath));\n  hbs.registerHelper('truncate', exports.truncate);\n  hbs.registerHelper('equals', exports.equals);\n};\n"
  },
  {
    "path": "views/includes/chevron_left.hbs",
    "content": "<svg fill=\"#000000\" height=\"48\" viewBox=\"0 0 24 24\" width=\"48\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z\"/>\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/chevron_right.hbs",
    "content": "<svg fill=\"#1976D2\" height=\"48\" viewBox=\"0 0 24 24\" width=\"48\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z\"/>\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/footer.hbs",
    "content": "{{!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. --}}\n\n<!-- Start of footer include. -->\n<!-- End of footer include. -->\n\n<footer class=\"dark-primary-background\">\n  <div class=\"offline-status\">Offline</div>\n  <ul>\n    <li>   \n      <a href=\"https://github.com/GoogleChrome/gulliver\" target=\"_blank\" rel=\"noopener\">\n        <img id=\"github-logo\" class=\"github-logo\" srcset=\"/img/GitHub-Mark-Light-24px.png 1x, /img/GitHub-Mark-Light-48px.png 2x\" width=\"24\" height=\"24\" alt=\"Github\" attribution=\"github\">\n      </a>\n    </li>\n    <li><a href=\"https://github.com/GoogleChrome/gulliver/issues/new\">feedback</a></li>\n    <li><a href=\"https://www.google.com/intl/en/policies/privacy/\">privacy</a></li>\n    <li>\n      <a href=\"http://feeds.feedburner.com/PwaDirectory\" target=\"_blank\" rel=\"noopener\">\n        <img id=\"rss-logo\" class=\"rss-logo\" srcset=\"/img/feed-icon-24px.png 1x, /img/feed-icon-48px.png 2x\" width=\"24\" height=\"24\" alt=\"RSS\">\n      </a>\n    </li>\n  </a>\n  </ul>\n<footer>\n<script async src=\"https://www.google-analytics.com/analytics.js\"></script>\n"
  },
  {
    "path": "views/includes/head.hbs",
    "content": "{{!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. --}}\n\n<!-- Start of head include. -->\n<title>{{title}}</title>\n<meta name=\"description\" content=\"{{description}}\">\n\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n\n<link rel=\"manifest\" href=\"/manifest.json\">\n<link rel=\"apple-touch-icon\" sizes=\"180x180\" href=\"/favicons/apple-touch-icon.png\">\n<link rel=\"icon\" type=\"image/png\" href=\"/favicons/favicon-32x32.png\" sizes=\"32x32\">\n<link rel=\"icon\" type=\"image/png\" href=\"/favicons/favicon-16x16.png\" sizes=\"16x16\">\n<link rel=\"mask-icon\" href=\"/favicons/safari-pinned-tab.svg\" color=\"#5bbad5\">\n<link rel=\"shortcut icon\" href=\"/favicons/favicon.ico\">\n<meta name=\"msapplication-config\" content=\"/favicons/browserconfig.xml\">\n<meta name=\"theme-color\" content=\"#7cc0ff\">\n<link rel=\"preconnect\" href=\"https://storage.googleapis.com\">\n<link rel=\"preconnect\" href=\"https://apis.google.com\">\n<link rel=\"preconnect\" href=\"https://ssl.gstatic.com\">\n<link rel=\"preconnect\" href=\"https://accounts.google.com\">\n<link rel=\"preconnect\" href=\"https://www.google-analytics.com\">\n<link rel=\"stylesheet\" type=\"text/css\" href=\"{{asset '/css/style.css'}}\">\n\n<!-- Origin Trial Token, feature = Web Share, origin = https://pwa-directory.appspot.com, expires = 2016-12-06 -->\n<meta http-equiv=\"origin-trial\" data-feature=\"Web Share\" data-expires=\"2016-12-06\" content=\"AuNW4MmMk4aT1N9HPDpSlUPkBywNReGpXpbTC+9nOE/rv6W6mToe7J1ZSmz35g+6BbPzjmE9gpBTZOrb2aXf6gwAAABgeyJvcmlnaW4iOiAiaHR0cHM6Ly9wd2EtZGlyZWN0b3J5LmFwcHNwb3QuY29tOjQ0MyIsICJmZWF0dXJlIjogIldlYlNoYXJlIiwgImV4cGlyeSI6IDE0ODEwNDQyMDJ9\">\n\n<script id=\"config\" type=\"application/json\">\n{{{configstring}}}\n</script>\n<script src=\"{{asset '/js/gulliver.js'}}\" defer></script>\n\n{{> metadata}}\n\n<!-- End of head include. -->\n"
  },
  {
    "path": "views/includes/header.hbs",
    "content": "{{!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. --}}\n\n<!-- Start of header include. -->\n<div class=\"navbar dark-primary-background\">\n  <div class=\"navbar-title\"><a id=\"title\" class=\"gulliver-content-only offline-aware\" href=\"/\">PWA Directory</a></div>\n  <div class=\"navbar-subtitle hide-on-mobile\">\n    <a id=\"subtitle\" href=\"/\" class=\"gulliver-content-only offline-aware\">A Directory of Progressive Web Apps</a>\n  </div>\n\n  <div class=\"notifications\">\n    <input type=\"checkbox\" id=\"notifications\" class=\"switch hidden\"/>\n    {{> notifications_active }}\n    {{> notifications_off }}\n    <label for=\"notifications\" class=\"switch-label box-shadow\"></label>\n  </div>\n</div>\n\n<div class=\"section dark-primary-background box-shadow\">\n  <a id=\"backlink\" class=\"back{{#unless backlink}} hidden{{/unless}}\">{{> chevron_left }}</a>\n  <div class=\"section-top\">\n    <form id=\"search\" class=\"search-form\">\n      <div class=\"search-group\">\n        <input id=\"search-input\" type=\"text\" name=\"search\" class=\"form-input box-shadow\" value=\"{{searchQuery}}\" placeholder=\"Search for PWAs\" aria-label=\"Seach for PWAs\">\n        <a id=\"search-icon\">{{> icon_search }}</a>\n      </div>\n    </form>\n      <button id=\"share-button\" class=\"toolbar-button button blue offline-aware hidden\" aria-label=\"Share\">\n        <span class=\"toolbar-icon\">{{> icon_share}}</span>\n      </button>\n      <button id=\"signin-button\" class=\"toolbar-button button blue offline-aware\" aria-label=\"Sign In\">\n        <span class=\"auth-button-label-login toolbar-icon\">{{> icon_log_in}}</span>\n      </button>\n      <button id=\"signout-button\" class=\"toolbar-button button blue offline-aware hidden\" aria-label=\"Sign Out\">\n        <span class=\"auth-button-label-logout toolbar-icon\">{{> icon_log_out}}</span>\n      </button>\n  </div>\n  <div id=\"tabs\" class=\"section-tabs{{#unless mainPage}} hidden{{/unless}}\">\n    <a id=\"installable\"\n      class=\"tab{{#if (equals currentTab 'installable')}} activetab{{/if}} gulliver-content-only offline-aware\"\n      href=\"/\">\n      Installable\n    </a>\n    <a id=\"newest\"\n      class=\"tab{{#if (equals currentTab 'newest')}} activetab{{/if}} gulliver-content-only offline-aware\"\n      href=\"/pwas/newest\">\n      New\n    </a>\n    <a id=\"score\"\n      class=\"tab{{#if (equals currentTab 'score')}} activetab{{/if}} gulliver-content-only offline-aware\"\n      href=\"/pwas/score\">\n      Score\n    </a>\n  </div>\n</div>\n<!-- End of header include. -->\n"
  },
  {
    "path": "views/includes/hourglass.hbs",
    "content": "<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M6 2v6h.01L6 8.01 10 12l-4 4 .01.01H6V22h12v-5.99h-.01L18 16l-4-4 4-3.99-.01-.01H18V2H6zm10 14.5V20H8v-3.5l4-4 4 4zm-4-5l-4-4V4h8v3.5l-4 4z\"/>\n    <path d=\"M0 0h24v24H0V0z\" fill=\"none\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/icon_log_in.hbs",
    "content": "<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M3 5v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2H5c-1.11 0-2 .9-2 2zm12 4c0 1.66-1.34 3-3 3s-3-1.34-3-3 1.34-3 3-3 3 1.34 3 3zm-9 8c0-2 4-3.1 6-3.1s6 1.1 6 3.1v1H6v-1z\"/>\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/icon_log_out.hbs",
    "content": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n  <path d=\"M19,3 C20.11,3 21,3.9 21,5 L21,8 L19,8 L19,5 L5,5 L5,19 L19,19 L19,16 L21,16 L21,19 C21,20.1 20.11,21 19,21 L5,21 C3.9,21 3,20.1 3,19 L3,5 C3,3.9 3.9,3 5,3 L19,3 Z M15.5,17 L20.5,12 L15.5,7 L14.09,8.41 L16.67,11 L7,11 L7,13 L16.67,13 L14.09,15.59 L15.5,17 Z\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/icon_search.hbs",
    "content": "<svg fill=\"#000000\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z\"/>\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/icon_share.hbs",
    "content": "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n    <path d=\"M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92 1.61 0 2.92-1.31 2.92-2.92s-1.31-2.92-2.92-2.92z\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/lighthouse.hbs",
    "content": "<section class=\"detail-general\">\n  <h3>Lighthouse  {{> score pwa}} </h3>  \n  <div class=\"chart\" type='lighthouse' {{#if pwa.id }} pwa={{pwa.id}} {{/if}}>\n    <div id=\"chart-missing\" class=\"hidden\">No lighthouse data available</div>\n  </div>\n  {{#if pwa.lighthouseScore}}\n  <div class=\"lighthouse-details\">\n    <a href=\"https://web-performance-dot-pwa-directory.appspot.com/lighthouse-viewer/?pwadir={{pwa.id}}\" target=\"_blank\" rel=\"noopener\">\n      <img srcset=\"/img/lh-logo-64.png 1x, /img/lh-logo-128.png 2x\" width=\"64\" height=\"64\" class=\"viewer-placeholder-logo\" alt=\"Lighthouse logo\">\n      <span>Lighthouse Report Viewer</span>\n    </a>\n  </div>\n  {{/if}}\n</section>\n"
  },
  {
    "path": "views/includes/metadata.hbs",
    "content": "<!-- Schema.org markup for Google+ -->\n<meta itemprop=\"name\" content=\"{{title}}\">\n<meta itemprop=\"description\" content=\"{{description}}\">\n<meta itemprop=\"image\" content=\"{{leader}}\">\n\n<!-- Open Graph data -->\n<meta property=\"fb:app_id\" content=\"205743429831145\" />\n<meta property=\"og:title\" content=\"{{title}}\" />\n<meta property=\"og:type\" content=\"article\" />\n<meta property=\"og:image\" content=\"{{leader}}\" />\n<meta property=\"og:url\" content=\"{{url}}\" />\n<meta property=\"og:description\" content=\"{{description}}\" />\n<meta property=\"og:site_name\" content=\"PWA Directory\" />\n<meta property=\"article:published_time\" content=\"{{datePublished}}\" />\n<meta property=\"article:modified_time\" content=\"{{dateModified}}\" />\n\n<!-- Metadata required by Google -->\n<script type=\"application/ld+json\">\n{\n  \"@context\": \"http://schema.org\",\n  \"@type\": \"CollectionPage\",\n  \"mainEntityOfPage\": \"{{url}}\",\n  \"headline\": \"{{title}}\",\n  \"datePublished\": \"{{datePublished}}\",\n  \"dateModified\": \"{{dateModified}}\",\n  \"description\": \"{{desc}}\",\n  \"author\": [{\n                 \"@type\": \"Person\",\n                 \"name\": \"Julian Toledo\"\n            },\n            {\n                 \"@type\": \"Person\",\n                 \"name\": \"Michael Stillwell\"\n            },\n            {\n                 \"@type\": \"Person\",\n                 \"name\": \"Andre Bandarra\"\n            },\n            {\n                 \"@type\": \"Person\",\n                 \"name\": \"Alberto Medina\"\n            }],\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"Google Developer Relations\",\n    \"logo\": {\n      \"@type\": \"ImageObject\",\n      \"url\": \"{{logo}}\",\n      \"width\": \"{{logoWidth}}\",\n      \"height\": \"{{logoHeight}}\"\n    }\n  },\n  \"image\": {\n    \"@type\": \"ImageObject\",\n    \"url\": \"{{leader}}\",\n    \"height\": \"{{leaderHeight}}\",\n    \"width\": \"{{leaderWidth}}\"\n  }\n}\n</script>\n"
  },
  {
    "path": "views/includes/notifications_active.hbs",
    "content": "<svg id=\"notifications_active\" fill=\"#000000\" height=\"32\" viewBox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n    <path d=\"M7.58 4.08L6.15 2.65C3.75 4.48 2.17 7.3 2.03 10.5h2c.15-2.65 1.51-4.97 3.55-6.42zm12.39 6.42h2c-.15-3.2-1.73-6.02-4.12-7.85l-1.42 1.43c2.02 1.45 3.39 3.77 3.54 6.42zM18 11c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2v-5zm-6 11c.14 0 .27-.01.4-.04.65-.14 1.18-.58 1.44-1.18.1-.24.15-.5.15-.78h-4c.01 1.1.9 2 2.01 2z\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/notifications_off.hbs",
    "content": "<svg id=\"notifications_off\" fill=\"#000000\" height=\"32\" viewBox=\"0 0 24 24\" width=\"32\" xmlns=\"http://www.w3.org/2000/svg\">\n    <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\n    <path d=\"M20 18.69L7.84 6.14 5.27 3.49 4 4.76l2.8 2.8v.01c-.52.99-.8 2.16-.8 3.42v5l-2 2v1h13.73l2 2L21 19.72l-1-1.03zM12 22c1.11 0 2-.89 2-2h-4c0 1.11.89 2 2 2zm6-7.32V11c0-3.08-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68c-.15.03-.29.08-.42.12-.1.03-.2.07-.3.11h-.01c-.01 0-.01 0-.02.01-.23.09-.46.2-.68.31 0 0-.01 0-.01.01L18 14.68z\"/>\n</svg>\n"
  },
  {
    "path": "views/includes/pagespeedinsight.hbs",
    "content": "<section class=\"detail-general\">\n  <h3>PageSpeed Insights</h3>\n  <div class=\"chart\" type='psi' {{#if pwa.id }} pwa={{pwa.id}} {{/if}}>\n    <div id=\"chart-missing\" class=\"hidden\">No PageSpeed Insights data available</div>\n  </div>\n</section>\n"
  },
  {
    "path": "views/includes/pwadetails.hbs",
    "content": "<a href=\"{{pwa.absoluteStartUrl}}\"  id=\"pwa\" class=\"detail-general\" style=\"background-color: {{pwa.backgroundColor}}; color: {{contrastColor pwa.backgroundColor}};\" target=\"_blank\" rel=\"noopener\">\n\n  <div id=\"pwa-logo-container\">\n    {{#if pwa.iconUrl128}}\n      <img src=\"{{pwa.iconUrl128}}\" width=\"128\" height=\"128\"/>\n    {{else}}\n    <svg class=\"fadeIn\" width=\"128\" height=\"128\">\n      <rect width=\"100%\" height=\"100%\" stroke-width=\"4\" fill=\"{{pwa.backgroundColor}}\" />\n      <text x=\"50%\" y=\"50%\" fill=\"{{contrastColor pwa.backgroundColor}}\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"128px\">{{firstLetter pwa.name}}</text>\n    </svg>\n    {{/if}}\n  </div>\n  <h3 id=\"pwa-name\" style=\"color: {{contrastColor pwa.backgroundColor}};\">{{pwa.displayName}}</h3>\n  <code id=\"pwa-url\">{{pwa.absoluteStartUrl}}</code>\n  <div id=\"pwa-description\">{{pwa.description}}</div>\n  <div class=\"dates\">{{#if pwa.created}}Added {{moment pwa.created}}, Updated {{moment pwa.updated}}{{/if}}</div>\n</a>\n"
  },
  {
    "path": "views/includes/score.hbs",
    "content": "<span class=\"score\">\n{{#if lighthouseScore}}\n{{lighthouseScore}}\n{{else}}\n<i class=\"fa fa-hourglass-half\" aria-hidden=\"true\" title=\"Lighthouse in progress\"></i>\n{{/if}}\n</span>\n"
  },
  {
    "path": "views/includes/webpagetest.hbs",
    "content": "<section class=\"detail-general\">\n  <h3>WebPageTest</h3>\n  <div class=\"chart\" type='wpt' {{#if pwa.id }} pwa={{pwa.id}} {{/if}}>\n    <div id=\"chart-missing\" class=\"hidden\">No WebPageTest Insights data available</div>\n  </div>\n</section>\n"
  },
  {
    "path": "views/pwas/form.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n\n{{#unless contentOnly}}\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    {{> head}}\n  </head>\n  <body>\n    {{> header}}\n    <div class=\"page-holder\">\n      <main class=\"page\">\n{{/unless}}\n        <form id=\"pwaForm\" action=\"\">\n          <div class=\"form-group\">\n            <label for=\"manifestUrl\">Manifest URL</label>\n\n            <input type=\"text\" name=\"manifestUrl\" id=\"manifestUrl\" class=\"form-input box-shadow\" value=\"{{manifestUrl}}\" autofocus required>\n\n            <button id=\"pwaSubmit\" type=\"submit\" class=\"button button-primary offline-aware signin-aware\">Save</button>\n          </div>\n        </form>\n\n        <div class=\"items\">\n\n        </div>\n\n        <template id=\"template-load-pwa\">\n          <a href=\"\" class=\"item card-pwa link-disabled box-shadow\" target=\"_blank\" rel=\"noopener\">\n              <div class=\"icon pwa-icon\"></div>\n              <div class=\"pwa-name\"></div>\n          </a>\n        </template>\n{{#unless contentOnly}}\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n{{/unless}}\n"
  },
  {
    "path": "views/pwas/list.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n\n{{#unless contentOnly}}\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    {{> head}}\n  </head>\n  <body>\n    {{> header}}\n    <div class=\"page-holder\">\n      <main class=\"page\">\n{{/unless}}      \n        <div class=\"toolbar\">\n        <a id=\"add\" href=\"/pwas/add\">\n            <button id=\"pwaAdd\" class=\"button-primary button offline-aware gulliver-content-only signin-aware\">\n            Add PWA\n            </button>\n        </a>\n        </div>\n\n        <div class=\"items\">\n            {{#each pwas}}\n            <a id=\"{{id}}\" class=\"item card-pwa offline-aware gulliver-content-only box-shadow\" href=\"/pwas/{{id}}\" style=\"background-color: {{backgroundColor}}; color:{{contrastColor backgroundColor}}\">\n            {{#if iconUrl64}}\n                <img class=\"pwa-icon\" src=\"{{iconUrl64}}\" srcset=\"{{iconUrl64}} 1x, {{iconUrl128}} 2x\" width=\"64\" height=\"64\" alt=\"logo for {{displayName}}\"/>\n            {{else}}\n                <svg class=\"pwa-icon\" width=\"64\" height=\"64\">\n                <rect width=\"64\" height=\"64\" stroke-width=\"4\" fill=\"{{backgroundColor}}\" />\n                <text x=\"32\" y=\"32\" fill=\"{{contrastColor backgroundColor}}\" alignment-baseline=\"middle\" text-anchor=\"middle\" font-size=\"32px\">{{firstLetter displayName}}</text>\n                </svg>\n            {{/if}}\n            <div class=\"pwa-name\">{{{truncate displayName 28}}}</div>\n            {{> score }}\n            </a>\n            {{/each}}\n        </div>\n\n        <div class=\"pager\">\n        {{#if hasPreviousPage}}\n          {{#if sortOrder}}\n            <a class=\"previous gulliver-content-only offline-aware\" href=\"?sort={{sortOrder}}{{#if previousPageNumber}}&page={{previousPageNumber}}{{/if}}\" aria-label=\"Previous Page\">\n          {{else}}\n            {{#if previousPageNumber}}\n            <a class=\"previous gulliver-content-only offline-aware\" href=\"?page={{previousPageNumber}}\" aria-label=\"Previous Page\">\n            {{else}}\n            <a class=\"previous gulliver-content-only offline-aware\" href=\"/\" aria-label=\"Previous Page\">\n            {{/if}}\n          {{/if}}\n              {{> chevron_left }}\n            </a>\n        {{/if}}\n        {{#if hasNextPage}}\n          {{#if sortOrder}}\n            <a class=\"next gulliver-content-only offline-aware\" href=\"?sort={{sortOrder}}&page={{nextPageNumber}}\" aria-label=\"Next Page\">\n          {{else}}\n            <a class=\"next gulliver-content-only offline-aware\" href=\"?page={{nextPageNumber}}\" aria-label=\"Next Page\">\n          {{/if}}\n              {{> chevron_right }}\n            </a>\n        {{/if}}\n        </div>\n{{#unless contentOnly}}\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n{{/unless}}\n"
  },
  {
    "path": "views/pwas/view-rss.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n    http://www.apache.org/licenses/LICENSE-2.0\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n \n<div style=\"text-align: center; vertical-align: middle; background-color:{{pwa.backgroundColor}}; color:{{contrastColor pwa.backgroundColor}}; padding: 16px;\">\n<a href=\"{{pwa.absoluteStartUrl}}\" target=\"_blank\" rel=\"noopener\">\n  {{#if pwa.iconUrl128}}\n    <img src=\"{{pwa.iconUrl128}}\" width=\"128\" height=\"128\"/>\n  {{/if}}\n</a>\n<a href=\"{{pwa.absoluteStartUrl}}\" target=\"_blank\" rel=\"noopener\" style=\"color:{{contrastColor pwa.backgroundColor}}; text-decoration: none;\"><h2>{{pwa.displayName}}</h2></a>\n<div>{{pwa.description}}</div>\n</div>\n\n<a href=\"https://pwa-directory.appspot.com/pwas/{{pwa.id}}\" target=\"_blank\" rel=\"noopener\">\n  <img style=\"vertical-align:middle\" src=\"https://pwa-directory.appspot.com/favicons/android-chrome-36x36.png\">\n  <span style=\"\">Open in PWA Directory</span>\n</a>\n"
  },
  {
    "path": "views/pwas/view.hbs",
    "content": "<!-- Copyright 2015-2016, Google, Inc.\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License. -->\n\n{{#unless contentOnly}}\n<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    {{> head}}\n  </head>\n  <body>\n    {{> header}}\n    <div class=\"page-holder\">\n      <main class=\"page\">\n{{/unless}}\n        {{> pwadetails pwa=pwa}}\n        {{> lighthouse lighthouse=lighthouse}}\n        {{> webpagetest}}\n        {{> pagespeedinsight}}\n        {{#if pwa.manifest}}\n        <div class=\"detail-general\">\n        <h3><a href=\"{{pwa.manifestUrl}}\" target=\"_blank\" rel=\"noopener\">MANIFEST</a></h3>\n        <pre>{{{highlightedJson rawManifestJson}}}</pre>\n        </div>\n        {{/if}}\n{{#unless contentOnly}}\n      </main>\n      <div class='page-loader'>\n      </div>\n    </div>\n    {{> footer}}\n  </body>\n</html>\n{{/unless}}\n"
  }
]